美文网首页嵌入式操作系统那些事儿
U-Boot/Linux设备树简明使用手册

U-Boot/Linux设备树简明使用手册

作者: 古斟布衣 | 来源:发表于2022-08-14 17:45 被阅读0次

    设备树(Device Tree)起源于IEEE1275 OpenFirmware和Power(PC) ePAPR标准,主要适用于Power(PC)架构,用于引导程序(Bootloader)向操作系统内核传递硬件配置和内核参数。目前,设备树已经扩展到U-Boot/Linux的Power(PC)/ARM/RISC-V/MIPS/x86等多个架构,从而大大简化了代码中的驱动的配置信息。

    1. 简介

    设备树的官网为devicetree.org,提供相关的FAQ和最新的标准等。对应的文档和库存放在github.com/devicetree-orgkernel.org/dtc

    设备树包括如下文件类型:

    • DTS(Device Tree Source):文本形式的设备树源文件,后缀为dts,可以包含头文件和DTSI文件,于配置如下信息:
      • 硬件信息, 包括设备的驱动兼容信息字符串、寄存器地址、中断请求、时钟、管脚复用、引用关系,以及CPU、Cache(高速缓存)、总线、物理内存等;
      • 内核参数,包括用于匹配BSP(板级支持包)和驱动的兼容信息字符传,启动参数,调试串口,从核启动方式等。
    • DTSI(Device Tree Source Include):文本形式的设备树源包含文件,后缀为dtsi,用于将若干个DTS文件公用的配置信息抽离出来,从而被多个DTS文件包含,从而简化DTS文件的配置;注意,DTSI文件可以包含其他DTSI文件,形成多级嵌套;
    • DTB(Devic Tree Blob):设备树源文件编译而成的二进制文件,用于被U-Boot/Linux访问以获取配置信息;
    • DTSO:安卓引入的扩展,用于对DTS文件中已经存在硬件信息进行修改,需要包含/plugin/;标签;
    • DTBO:安卓引入的扩展,从DTSO编译而来。

    2. 设备树配置

    设备树本质是一个树形结构,如下所示:

    /dts-v1/;
    
    #include "xxx.dtsi"
    
    / {
        model = "board name";
        compatible = "vendor,BSP name";
    
        aliases {
            ts0 = &tempsensor0;
        };
    
        cpus {
            #address-cells = <1>;
            #size-cells = <0>;
    
            cpu0: cpu@0 {
                    device_type = "cpu";
                    compatible = "arm,cortex-a72";
                    enable-method = "psci";
                    reg = <0x0>;
                    clocks = <&clockgen 1 0>;
                    d-cache-size = <0x8000>;
                    ...
                };
            ...
            };
    
        memory@0 {
            device_type = "memory";
            reg = < 0x00 0x80000000 0x00 0x40000000 0x00 0x00 0x00 0x00 >;
        };
    
        chosen {
            stdout-path = "/soc/serial@48020000";
            bootargs = "console=ttyS0,115200n8 root=xxx rw rootfstype=ext4 ...";
        };
        ...
        soc {
            #address-cells = <2>;
            #size-cells = <2>;
            ...
            i2c0: i2c@2000000 {
                compatible = "fsl,vf610-i2c";
                #address-cells = <1>;
                #size-cells = <0>;
                reg = <0x0 0x2000000 0x0 0x10000>;
                interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
                clock-names = "i2c";
                clocks = <&clockgen 4 15>;
                scl-gpios = <&gpio2 15 GPIO_ACTIVE_HIGH>;
    
                tempsensor0: i2c3dev0@62 {
                    compatible = "nxp,sa56004";
                    reg = <0x4c>;
                    vcc-supply = <&sb_3v3>;
                };
            ...
            };
            ...
        };
        ...
    };
    

    其中:

    • 所有节点都从根节点/开始逐级包含,名称和路径都不得重复;
    • 每个节点的路径都是从根节点开始计算,例如/soc/i2c@2000000/i2c3dev0@62:为了方便使用,可以为每个节点加上1个标签,例如tempsensor0clockgen
    • 每个节点都包含若干个属性,用于配置设备驱动软件需要的各种信息;这些属性与软件紧密相关,没有统一的标准;其中的compatible属性包含若干个兼容字符,按照从高到低的优先级依次排列,用于匹配到最合适的驱动软件。

    设备树的其他具体配置规范可以参考github.com/devicetree-org/devicetree-specification

    3. 设备树访问

    设备树源文件(DTS/DTSO)通过开源工具DTC(Devic Tree compiler)转换为为DTB文件或者包含DTB二进制数据的汇编文件,进而与U-Boot/Linux等编译在一起,并可以通过libfdt库从DTB文件和二进制数据中获取信息。

    DTC工具源码托存在kernel.org/dtc,包含DTC工具和libfdt库,也可以使用操作系统自带的工具安装,例如:

    sudo apt install device-tree-compiler

    DTC工具除了可以从DTS生成DTB,还可以从DTB文件和文件系统(/sys/firmware/devicetree/base或者/proc/device-tree)生成DTS文件,例如:

    $ dtc -h                                                    # 查看帮助信息
    $ dtc -I dts -O dtb -o xxx.dtb xxx.dts                      # 从DTS生成DTB
    $ dtc -I dtb -O dts -o xxx.dts xxx.dtb                      # 从DTB生成DTS
    $ dtc -I fs -O dts -o xxx.dts /sys/firmware/devicetree/base # 从文件系统生成DTS
    

    DTC工具包含的libfdt库用于访问通过DTB文件或者包含DTB二进制数据的汇编文件形式加载到内存或者其他存储介质中的设备树信息中,其接口定义在libfdt/libfdt.h

    4. U-Boot/Linux设备树获取

    有源码的情况下,设备树文件通常可以通过搜索后缀为dtsdtsidtso的文件来获取。其中:

    • 对于U-Boot,设备树文件通常可以defconfig配置文件中CONFIG_DEFAULT_DEVICE_TREE对应的设备树名字或来寻找者根据defconfig配置文件的名字来猜测;

    • 对于Linux,设备树文件的名字通常可以从内核加载信息里面根据Machine model即DTS中的根节点下的model属性来定位DTS文件:

      [    0.000000] Booting Linux on physical CPU 0x0
      ...
      [    0.000000] CPU: ARMv7 Processor [412fc0f2] revision 2 (ARMv7), cr=30c5387d
      [    0.000000] CPU: div instructions available: patching division code
      [    0.000000] CPU: PIPT / VIPT nonaliasing data cache, PIPT instruction cache
      [    0.000000] OF: fdt:Machine model: TL570x-EVM
      

    没有源码的情况下,U-Boot的设备树暂时没有办法获取,但是Linux的设备树却可以通过U-Boot来获取:

    • 获取设备树地址:

      • 对于Legacy加载模式,Linux内核和DTB文件通常使用bootm <内核地址> <initrd文件系统地址> <DTB地址>,其中initrd文件系统地址可能用-代替;因此可以先执行bootm之前的命令,使得DTB文件被加载到内存中去;

      • 对于FIT加载模式,Linux内核和DTB文件被编译在一个镜像中,加载信息中fdt镜像对应的Data Start地址即FIT镜像加载后设备树的地址,因此可以先执行bootm之前的命令,使得FIT镜像被加载到内存中去,进而将设备树拷贝到最终的内存位置即Booting using the fdt blob at 0xXXXXXXXX包含的地址:

        ## Loading kernel from FIT Image at a0000000 ...
          Using 'ls1046afrwy' configuration
          Trying 'kernel' kernel subimage
            ...
        ## Loading ramdisk from FIT Image at a0000000 ...
          Using 'ls1046afrwy' configuration
          Trying 'initrd' ramdisk subimage
            ...
        ## Loading fdt from FIT Image at a0000000 ...
          Using 'ls1046afrwy' configuration
          Trying 'ls1046afrwy-dtb' fdt subimage
            Description:  ls1046afrwy-dtb
            Type:         Flat Device Tree
            Compression:  uncompressed
            Data Start:   0xa19d41b8
            Data Size:    31569 Bytes = 30.8 KiB
            Architecture: AArch64
            Load Address: 0x90000000
            Hash algo:    crc32
            Hash value:   d30014cb
          Verifying Hash Integrity ... crc32+ OK
          Loading fdt from 0xa19d41b8 to 0x90000000
          Booting using the fdt blob at 0x90000000
          ...
        Starting kernel ...
        
    • 使用如下命令设置设备树基地址到上一步发现的设备树地址并dump:

      => fdt addr 90000000; fdt print
      
    • 如果U-Boot支持U盘、SD卡等,还可以直接将设备树文件转存上去,以USB为例(SD卡类似):

      => usb start
      starting USB...
      USB0:   Register 2000140 NbrPorts 2
      Starting the controller
      USB XHCI 1.00
      scanning bus 0 for devices... 2 USB Device(s) found
          scanning usb for storage devices... 1 Storage Device(s) found
      => usb dev
      
      IDE device 0: Vendor: SanDisk  Rev: 0    Prod: Extreme Pro
                  Type: Removable Hard Disk
                  Capacity: 122112.0 MB = 119.2 GB (250085376 x 512)
      => usb part
      
      Partition Map for USB device 0  --   Partition Type: DOS
      
      Part    Start Sector    Num Sectors     UUID            Type
      1     2048            195309568       000930ef-01     0b Boot
      2     195313662       54769666        000930ef-02     05 Extd
      5     195313664       54769664        000930ef-05     83
      => fatwrite usb 0:1 ${fdtaddr} am5708_uboot.dtb 16F67
          writing am5708_uboot.dtb
          94055 bytes written
      

      其中,用于转存的USB分区为IDE device 0上的分区1,因此使用usb 0:1来写入;对于SD卡来说,不需要执行usb start命令,只需要确认设备和分区即可。

    此外,如果Linux文件系统中包含/sys/firmware/devicetree/base或者/proc/device-tree目录,则设备树还可以直接通过文件系统来获取:

    $ dtc -I fs -O dts -o xxx.dts /sys/firmware/devicetree/base
    

    相关文章

      网友评论

        本文标题:U-Boot/Linux设备树简明使用手册

        本文链接:https://www.haomeiwen.com/subject/pvncgrtx.html