Linux 内核:设备树中的特殊节点

背景

在解析设备树dtb格式的时候,发现了这个,学习一下。

参考:

介绍

常见的特殊节点有

  • aliases:用于定义别名,目的就是为了方便访问节点
  • chosen :chosen 并不是一个真实的设备, chosen 节点主要是为了 uboot 向 Linux 内核传递数据,重点是 bootargs 参数。一般.dts 文件中 chosen 节点通常为空或者内容很少

以我之前调试过的zynq平台为例。

  1. / {
  2. model = "ZynqMP ZCU104 RevA";
  3. compatible = "xlnx,zynqmp-zcu104-revA", "xlnx,zynqmp-zcu104", "xlnx,zynqmp";
  4. aliases {
  5. ethernet0 = &gem3;
  6. gpio0 = &gpio;
  7. i2c0 = &i2c1;
  8. mmc0 = &sdhci1;
  9. rtc0 = &rtc;
  10. serial0 = &uart0;
  11. serial1 = &uart1;
  12. serial2 = &dcc;
  13. spi0 = &qspi;
  14. usb0 = &usb0;
  15. };
  16. chosen {
  17. bootargs = "earlycon";
  18. stdout-path = "serial0:115200n8";
  19. };
  20. memory@0 {
  21. device_type = "memory";
  22. reg = <0x0 0x0 0x0 0x80000000>;
  23. };
  24. };

aliases 子节点

单词 aliases 的意思是“别名”,因此 aliases 节点用于定义别名,目的就是为了方便访问节点。

不过我们一般会在节点命名的时候会加上 label,然后通过&label来访问节点,这样也很方便,而且设备树里面大量的使用&label 的形式来访问节点。

  1. / {
  2. model = "ZynqMP ZCU104 RevA";
  3. compatible = "xlnx,zynqmp-zcu104-revA", "xlnx,zynqmp-zcu104", "xlnx,zynqmp";
  4. aliases {
  5. // ...
  6. spi0 = &qspi;
  7. };
  8. // ...
  9. };
  10. // ...
  11. &qspi {
  12. status = "okay";
  13. flash@0 {
  14. compatible = "m25p80", "spi-flash"; /* n25q512a 128MiB */
  15. #address-cells = <1>;
  16. #size-cells = <1>;
  17. reg = <0x0>;
  18. spi-tx-bus-width = <1>;
  19. spi-rx-bus-width = <4>;
  20. spi-max-frequency = <108000000>; /* Based on DC1 spec */
  21. partition@qspi-fsbl-uboot { /* for testing purpose */
  22. label = "qspi-fsbl-uboot";
  23. reg = <0x0 0x100000>;
  24. };
  25. partition@qspi-linux { /* for testing purpose */
  26. label = "qspi-linux";
  27. reg = <0x100000 0x500000>;
  28. };
  29. partition@qspi-device-tree { /* for testing purpose */
  30. label = "qspi-device-tree";
  31. reg = <0x600000 0x20000>;
  32. };
  33. partition@qspi-rootfs { /* for testing purpose */
  34. label = "qspi-rootfs";
  35. reg = <0x620000 0x5E0000>;
  36. };
  37. };
  38. };

chosen 子节点

chosen 并不是一个真实的设备, chosen 节点主要是为了 uboot 向 Linux 内核传递数据,重点是 bootargs 参数。

一般.dts 文件中 chosen 节点通常为空或者内容很少。

  1. / {
  2. model = "ZynqMP ZCU104 RevA";
  3. compatible = "xlnx,zynqmp-zcu104-revA", "xlnx,zynqmp-zcu104", "xlnx,zynqmp";
  4. chosen {
  5. bootargs = "earlycon";
  6. stdout-path = "serial0:115200n8";
  7. };
  8. // ...
  9. };

从上面中可以看出, chosen 节点设置了

  • stdout-path”,表示标准输出使用 serial0
  • bootargs,表示用于Linux的启动参数

uboot、linux与bootargs

在支持设备树的嵌入式系统中,实际上:

  • uboot基本上可以不通过显式的bootargs=xxx来传递给内核,而是在env拿出,并存放进设备树中的chosen节点中
  • Linux也开始在设备树中的chosen节点中获取出来,

这样子就可以做到针对uboot与Linux在bootargs传递上的统一。

uboot 与 chosen

结论:uboot 会自己在chosen 节点里面添加了 bootargs 属性!并且设置 bootargs 属性的值为 bootargs环境变量的值。

因为在启动 Linux 内核之前,只有 uboot 知道 bootargs 环境变量的值,并且 uboot也知道.dtb 设备树文件在 DRAM 中的位置,所以uboot可以这样子做。

  1. // common/fdt_support.c
  2. int fdt_chosen(void *fdt)
  3. {
  4. int nodeoffset;
  5. int err;
  6. char *str; /* used to set string properties */
  7. err = fdt_check_header(fdt);
  8. if (err < 0) {
  9. printf("fdt_chosen: %s\n", fdt_strerror(err));
  10. return err;
  11. }
  12. /* find or create "/chosen" node. */
  13. // 从设备树(.dtb)中找到 chosen 节点,
  14. // 如果没有找到的话就会自己创建一个 chosen 节点
  15. nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen");
  16. if (nodeoffset < 0)
  17. return nodeoffset;
  18. // 读取 uboot 中 bootargs 环境变量的内容。
  19. str = getenv("bootargs");
  20. if (str) {
  21. // 向 chosen 节点添加 bootargs 属性,并且 bootargs 属性的值就是环境变量 bootargs 的内容
  22. err = fdt_setprop(fdt, nodeoffset, "bootargs", str,
  23. strlen(str) + 1);
  24. if (err < 0) {
  25. printf("WARNING: could not set bootargs %s.\n",
  26. fdt_strerror(err));
  27. return err;
  28. }
  29. }
  30. return fdt_fixup_stdout(fdt, nodeoffset);
  31. }

调用流程:

  1. bootz
  2. do_bootz()
  3. do_bootm_states()
  4. boot_selected_os()
  5. boot_fn() -> do_bootm_linux
  6. // 准备启动Linux之前的一些工作
  7. boot_prep_linux()
  8. image_setup_linux()
  9. image_setup_libfdt()
  10. fdt_chosen()

上图中框起来的部分就是函数 do_bootm_linux 函数的执行流程,也就是说do_bootm_linux 函数会通过一系列复杂的调用,最终通过 fdt_chosen 函数在 chosen 节点中加入了 bootargs 属性。

这样子,Linux内核在启动的时候,就可以根据bootargs来做自己要做的事情。

linux与 chosen

以arm架构为例。

Linux会根据dtb中的chosen中的bootargs属性来重写cmd_lines

  1. int __init early_init_dt_scan_chosen(unsigned long node, const char *uname,
  2. int depth, void *data)
  3. {
  4. unsigned long l;
  5. char *p;
  6. pr_debug("search \"chosen\", depth: %d, uname: %s\n", depth, uname);
  7. if (depth != 1 || !data ||
  8. (strcmp(uname, "chosen") != 0 && strcmp(uname, "chosen@0") != 0))
  9. return 0;
  10. early_init_dt_check_for_initrd(node);
  11. /* Retrieve command line */
  12. // 找到设备树中的的chosen节点中的bootargs,并作为cmd_line
  13. p = of_get_flat_dt_prop(node, "bootargs", &l);
  14. if (p != NULL && l > 0)
  15. strlcpy(data, p, min((int)l, COMMAND_LINE_SIZE));
  16. // ...
  17. pr_debug("Command line is: %s\n", (char*)data);
  18. /* break now */
  19. return 1;
  20. }

流程如下:

  1. start_kernel
  2. setup_arch(&command_line);
  3. setup_machine_fdt();
  4. early_init_dt_scan_nodes();
  5. early_init_dt_scan_chosen();

Linux 内核:设备树中的特殊节点的更多相关文章

  1. Linux内核 设备树操作常用API【转】

    转自:https://www.linuxidc.com/Linux/2017-02/140818.htm 一文中介绍了设备树的语法,这里主要介绍内核中提供的操作设备树的API,这些API通常都在&qu ...

  2. Linux内核 设备树操作常用API

    Linux设备树语法详解一文中介绍了设备树的语法,这里主要介绍内核中提供的操作设备树的API,这些API通常都在"include/of.h"中声明. device_node 内核中 ...

  3. Linux 内核设备驱动

    设备模型跟踪所有对系统已知的驱动. 这个跟踪的主要原因是使驱动核心能匹配驱动和新 设备. 一旦驱动在系统中是已知的对象, 但是, 许多其他的事情变得有可能. 设备驱动可 输出和任何特定设备无关的信息和 ...

  4. Linux 内核 设备结构嵌入

    设备结构包含设备模型核心需要的来模型化系统的信息. 大部分子系统, 但是, 跟踪关于 它们驻留的设备的额外信息. 结果, 对设备很少由空设备结构所代表; 相反, 这个结构, 如同 kobject 结构 ...

  5. Linux 内核设备属性

    sysfs 中的设备入口可有属性. 相关的结构是: struct device_attribute { struct attribute attr; ssize_t (*show)(struct de ...

  6. Linux 内核设备注册

    通常的注册和注销函数在: int device_register(struct device *dev); void device_unregister(struct device *dev); 我们 ...

  7. linux设备驱动程序-设备树(3)-设备树多级子节点的转换

    linux设备驱动程序--设备树多级子节点的转换 在上一章:设备树处理之--device_node转换成platform_device中,有提到在设备树的device_node到platform_de ...

  8. 【转】6.4.6 将驱动编译进Linux内核进行测试

    原文网址:http://www.apkbus.com/android-98520-1-1.html 前面几节都是将Linux驱动编译成模块,然后动态装载进行测试.动态装载驱动模块不会随着Android ...

  9. [翻译]Linux 内核里的数据结构 —— 基数树

    目录 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree Linux内核基数树API 链接 Linux 内核里的数据结构 -- 基数树 基数树 Radix tree 正如你所知道 ...

  10. Linux内核device结构体分析

    1.前言 Linux内核中的设备驱动模型,是建立在sysfs设备文件系统和kobject上的,由总线(bus).设备(device).驱动(driver)和类(class)所组成的关系结构,在底层,L ...

随机推荐

  1. k8s管理应用

  2. Solution Set - 图上问题

    CF360E Link&Submission. 首先显然可以选择的边的权值一定会取端点值.事实上,第一个人经过的边选最小,第一个人不经过的边选最大,这样一定不劣.进一步,如果 \(s_1\) ...

  3. list转json tree的工具类

    package com.glodon.safety.contingency.job; import com.alibaba.fastjson.JSON; import com.alibaba.fast ...

  4. [4]自定义Lua解析器管理器-------演化脚本V0.7

    [4]自定义Lua解析器管理器-------演化脚本V0.7 使用自定义委托来调用lua脚本中的多返回值函数和长参数类型的函数. 先看代码,依旧是上篇文章中所贴的脚本.新增调用两个函数testFunc ...

  5. Hugging Face 与 Wiz Research 合作提高人工智能安全性

    我们很高兴地宣布,我们正在与 Wiz 合作,目标是提高我们平台和整个 AI/ML 生态系统的安全性. Wiz 研究人员 与 Hugging Face 就我们平台的安全性进行合作并分享了他们的发现. W ...

  6. 生物医学顶刊论文(JBHI-2024):TransFOL:药物相互作用中复杂关系推理的逻辑查询模型

    (2024.5.17)JBHI-TransFOL:药物相互作用中复杂关系推理的逻辑查询模型 论文题目:TransFOL: A Logical Query Model for Complex Relat ...

  7. Kubernetes集群中配置Ingress支持HTTPS访问(一):cfssl

    目录 一.系统环境 二.前言 三.对称加密和非对称加密简介 四.什么是HTTPS 五.Ingress简介 六.配置ingress对外发布服务 6.1 安装NGINX ingress controlle ...

  8. 三:nacos的配置中心

    配置中心的使用: 编辑当前项目的pom.xml,加入必要的依赖配置 <!-- spring-cloud-alibaba-dependencies 依赖同注册中心 --> <depen ...

  9. 莫烦tensorflow学习记录 (6)卷积神经网络 CNN (Convolutional Neural Network)

    卷积 和 神经网络 莫烦大佬的原文章https://mofanpy.com/tutorials/machine-learning/tensorflow/intro-CNN/ 我的理解就是千层饼,鸡蛋烧 ...

  10. [快速阅读六] 统计内存数据中二进制1的个数(SSE指令集优化版).

    关于这个问题,网络上讨论的很多,可以找到大量的资料,我觉得就就是下面这一篇讲的最好,也非常的全面:          统计无符号整数二进制中 1 的个数(Hamming Weight) 在指令集不参与 ...