This page is dedicated to the project bringing the flattened device tree (FDT) technology to the FreeBSD. For details contact RafalJaworowski. Work on this project was kindly sponsored by the FreeBSD Foundation.
The objective of this project is to provide FreeBSD with the ability to use flattened device trees for description of hardware resources of a computer system and their dependencies, in a platform-neutral and portable way. The primary consumer of this functionality are embedded systems (based on ARM, AVR32, MIPS, PowerPC), but the mechanism can be used in all cases when hardware resources cannot be self-enumerated (as such could be seen as a future replacement for device.hints used in the legacy PC world).
The code has been committed to the FreeBSD SVN repository as of r210053, and all development is now supposed to happen in the SVN HEAD. The obsolete, no longer maintained original project development P4 branch can be found here.
In embedded world there is great variety of systems based on similar silicon chips, but designed into custom boards and devices, where connections of individual components are different and there are no conventions or rules, even across members of the same family of products, for the interconnectios layout and resources allocation. Furthermore, some buses and interconnects are not self-enumerable by definition (unlike PCI or USB), and there has to be some prior knowledge about how they are connected and what their unique identification is. Some of the examples of typical problems are the following:
The concept of a flattened device tree (FDT) is an established and mature way of handling such problems and has been successfully used for Linux/powerpc. It has also been adopted as a basis for Power.org's embedded platform reference specification (ePAPR).
The idea is inherited from Open Firmware IEEE 1275 device-tree notion (part of the regular Open Firmware implementation), but it allows to use device tree mechanism on all systems (not based on OF). It boils down to the following:
This section is meant as a convenient helper for people, who want to quickly check out the idea of using FDT approach. It shows only the statically embedded DTB approach, with SHEEVAPLUG kernel config as an example. For other options and more detailed descriptions please see relevant parts of this manual.
$ cd ${SRC}
$ make -j 8 buildworld TARGET_ARCH=arm -DWITH_FDT
This creates the dtc compiler utility and FDT-enabled loader(8).
Enable support in the kernel, make sure the sys/arm/conf/SHEEVAPLUG kernel config file has the following entries:
# Enable FDT support. options FDT makeoptions FDT_DTS_FILE=sheevaplug.dts options FDT_DTB_STATIC
$ make buildkernel TARGET_ARCH=arm KERNCONF=SHEEVAPLUG
The kernel build process includes creation (compilation) of the DTS file into a binary form (DTB), so no explicit steps are required to produce the device tree blob.
The kernel with statically embedded DTB does not require any additional handling and is ready to be bootstrapped in a standard way.
The device tree source is a text file which describes hardware resources of a computer system in a human-readable form. See example snippets of a device tree source (DTS) featuring description of all major components (CPU, memory, system-on-chip peripherals, IRQ assignments etc.) as device tree nodes and their properties.
The default locations of DTS files are the following subdirectories of the FreeBSD source repository:
sys/dts sys/gnu/dts
For regular use case the textual device tree description (DTS file) is first converted (compiled) into a binary object (the device tree blob i.e. the DTB), which is handed over to the final consumer (kernel) for parsing and processing of its contents.
A stand-alone tool executed on the host, which transforms (compiles) a textual description of a device tree (DTS) into a binary object (DTB).
Helper library providing basic routines for parsing the device tree blob and retrieving data. It is integral part of the dtc tool, and can be used in any other code which needs to process the DTB.
In FreeBSD for FDT-enabled platforms, libfdt is therefore also used by loader(8) and kernel for accessing the device tree blob.
The typical way of building the device tree compiler is supplying WITH_FDT setting during buildworld procedure, either via src.conf(5) or manually. Note the dtc tool has to be built at bootstrap tools building stage of the buildworld procedure, because the build host is not guaranteed to have the compiler installed and readily available. This is similar to other elementary tools required during further stages of the build process (like config, make and others).
$ cd $SRC $ make buildworld -DWITH_FDT
Note the WITH_FDT setting is turned on by default for ARM and PowerPC. In order to opt out FDT support on these platforms supply WITHOUT_FDT during buildworld procedure.
In order to run a kernel driven by a flattened device tree configuration, there are two required items:
The DTB can either be statically embedded as part of the kernel image or a stand alone file, explicitly loaded and handled by loader(8).
There are a couple of FreeBSD kernel options available to manage FDT support.
# Flattened Device Tree (FDT) support. # # Enable FDT support. options FDT # Provide a preferred (default) device tree source (DTS) file for the kernel. # The indicated DTS file will be converted (compiled) into a binary form # during kernel build stage. makeoptions FDT_DTS_FILE=board.dts # Statically embed device tree blob (DTB) into a kernel image. This option # allows using device tree on platforms which do not (cannot) run loader(8); # in these cases we need to embed the DTB as part of kernel data. This option # requires a DTS file to be specified with FDT_DTS_FILE makeoption. options FDT_DTB_STATIC
Note the FDT_DTS_FILE parameter is a file name specified relative to one of the default directories:
sys/dts sys/gnu/dts
Currently the build system can accept DTS files only from these locations.
On platforms capable of running loader(8) the default scenario is to use a stand-alone DTB file, which is handled by the loader and handed over to kernel at boot time. In this approach, a DTB should be created at kernel build time:
$ make builddtb FDT_DTS_FILE=mpc8572ds.dts
Platforms without loader(8) support need to include the DTB file as integral part of the kernel image. For this case the FDT_DTB_STATIC option needs to be supplied in kernel config, as well as the FDT_DTS_FILE makeoption (which becomes mandatory in this scenario). At the buildkernel time the DTB file is automatically created from the specified DTS and embedded in the kernel without any user assistance.
The kernel installation procedure is no different for an FDT-enabled kernel: standard installkernel procedure applies. However, for the stand-alone DTB approach (non-static) there are caveats regarding DTB file installation:
The following loader(8) commands are available for the device tree support
fdt cd <fdt_path> fdt header fdt ls [fdt_path] fdt mknode [fdt_path/]<node_name> fdt mkprop [node_path/]<property_name> <string | [ byte1 byte2 .. ] | <uint32_1 uint32_2 .. > > fdt prop [node_path/[prop_name value_to_set]] fdt pwd fdt rm [node_path/]<node_name | property_name>
loader> load -t dtb boot/mpc8555cds.dtb loader> lsmod ... 0x162f92c: boot/mpc8555cds.dtb (dtb, 0x1eb2) loader>
loader> fdt header Flattened device tree header (0x162f92c): magic = 0xd00dfeed size = 7858 off_dt_struct = 0x00000038 off_dt_strings = 0x000018ac off_mem_rsvmap = 0x00000028 version = 17 last compatible version = 16 boot_cpuid = 0 size_dt_strings = 518 size_dt_struct = 6260 loader>
loader> fdt ls /aliases /cpus /cpus/PowerPC,8555@0 /memory /soc8555@e0000000 /soc8555@e0000000/ecm-law@0 /soc8555@e0000000/ecm@1000 /soc8555@e0000000/memory-controller@2000 /soc8555@e0000000/l2-cache-controller@20000 /soc8555@e0000000/i2c@3000 /soc8555@e0000000/dma@21300 /soc8555@e0000000/dma@21300/dma-channel@0 /soc8555@e0000000/dma@21300/dma-channel@80 /soc8555@e0000000/dma@21300/dma-channel@100 /soc8555@e0000000/dma@21300/dma-channel@180 /soc8555@e0000000/ethernet@24000 /soc8555@e0000000/ethernet@24000/mdio@520 /soc8555@e0000000/ethernet@24000/mdio@520/ethernet-phy@0 /soc8555@e0000000/ethernet@24000/mdio@520/ethernet-phy@1 /soc8555@e0000000/ethernet@24000/mdio@520/tbi-phy@11 /soc8555@e0000000/ethernet@25000 /soc8555@e0000000/ethernet@25000/mdio@520 /soc8555@e0000000/ethernet@25000/mdio@520/tbi-phy@11 /soc8555@e0000000/serial@4500 /soc8555@e0000000/serial@4600 /soc8555@e0000000/crypto@30000 /soc8555@e0000000/pic@40000 /soc8555@e0000000/cpm@919c0 /soc8555@e0000000/cpm@919c0/muram@80000 /soc8555@e0000000/cpm@919c0/muram@80000/data@0 /soc8555@e0000000/cpm@919c0/brg@919f0 /soc8555@e0000000/cpm@919c0/pic@90c00 /pci@e0008000 /pci@e0008000/i8259@19000 /pci@e0009000 loader>
loader> load -t dtb boot/mpc8555cds.dtb loader> boot -s
See full booting log.
The fdtbus and simplebus devices represent FDT hierarchy translated into newbus paradigm:
# devinfo
nexus0
fdtbus0
lbc0
cfi0
cfid0
cfi1
cfid1
rtc0
simplebus0
i2c0
iicbus0
iic0
tsec0
miibus0
ciphy0
tsec1
miibus1
ciphy1
uart0
uart1
sec0
openpic0
#
loader> fdt prop /soc8555@e0000000 #address-cells = <0x00000001> #size-cells = <0x00000001> device_type = "soc" compatible = "simple-bus" ranges = <0x00000000 0xe0000000 0x00100000> bus-frequency = <0x00000000> loader>
loader> fdt prop /cpus/PowerPC,8572@0 device_type = "cpu" reg = <0x00000000> d-cache-line-size = <0x00000020> i-cache-line-size = <0x00000020> d-cache-size = <0x00008000> i-cache-size = <0x00008000> timebase-frequency = <0x00000000> bus-frequency = <0x23c34600> clock-frequency = <0x00000000> next-level-cache = <0x00000001>
loader> fdt prop /cpus/PowerPC,8572@0/clock-frequency <15000000>
loader> fdt prop /cpus/PowerPC,8572@0 ... clock-frequency = <0x00e4e1c0> ...
loader> fdt prop /aliases ethernet0 = "/soc8572@ffe00000/ethernet@24000" ethernet1 = "/soc8572@ffe00000/ethernet@25000" ethernet2 = "/soc8572@ffe00000/ethernet@26000" ethernet3 = "/soc8572@ffe00000/ethernet@27000" serial0 = "/soc8572@ffe00000/serial@4500" serial1 = "/soc8572@ffe00000/serial@4600" pci0 = "/pcie@ffe08000" pci1 = "/pcie@ffe09000" pci2 = "/pcie@ffe0a000"
loader> fdt rm /aliases/pci2
loader> fdt prop /aliases ethernet0 = "/soc8572@ffe00000/ethernet@24000" ethernet1 = "/soc8572@ffe00000/ethernet@25000" ethernet2 = "/soc8572@ffe00000/ethernet@26000" ethernet3 = "/soc8572@ffe00000/ethernet@27000" serial0 = "/soc8572@ffe00000/serial@4500" serial1 = "/soc8572@ffe00000/serial@4600" pci0 = "/pcie@ffe08000" pci1 = "/pcie@ffe09000"
loader> fdt mkprop /aliases/pci2 "/pcie@ffe0a000"
loader> fdt prop /aliases pci2 = "/pcie@ffe0a000" ethernet0 = "/soc8572@ffe00000/ethernet@24000" ethernet1 = "/soc8572@ffe00000/ethernet@25000" ethernet2 = "/soc8572@ffe00000/ethernet@26000" ethernet3 = "/soc8572@ffe00000/ethernet@27000" serial0 = "/soc8572@ffe00000/serial@4500" serial1 = "/soc8572@ffe00000/serial@4600" pci0 = "/pcie@ffe08000" pci1 = "/pcie@ffe09000"
loader> fdt ls /aliases /cpus /cpus/PowerPC,8572@0 /cpus/PowerPC,8572@1 /memory /localbus@ffe05000 /localbus@ffe05000/nor@0,0 /localbus@ffe05000/nor@0,0/ramdisk@0 ...
loader> fdt mknode /chosen
loader> fdt ls /chosen /aliases /cpus /cpus/PowerPC,8572@0 /cpus/PowerPC,8572@1 /memory /localbus@ffe05000 /localbus@ffe05000/nor@0,0 /localbus@ffe05000/nor@0,0/ramdisk@0 ...
loader> fdt rm /chosen
loader> fdt ls /aliases /cpus /cpus/PowerPC,8572@0 /cpus/PowerPC,8572@1 /memory /localbus@ffe05000 /localbus@ffe05000/nor@0,0 /localbus@ffe05000/nor@0,0/ramdisk@0 ...
Marvell>> tftpboot 900000 sheevaplug/kernel.bin
Using egiga0 device
TFTP from server 10.0.0.204; our IP address is 10.0.2.26
Filename 'sheevaplug/kernel.bin'.
Load address: 0x900000
Loading: #################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
#################################################################
########################################
done
Bytes transferred = 2864724 (2bb654 hex)
Marvell>> go 900000
From the device tree perspective, an FDT-driven platform is not very much different than a genuine Open Firmware. The user can inspect the device tree with ofwdump(8) command in base. The FDT is internally hooked into kernel ofw_bus infrastructure, so it is accessible to user process through the /dev/openfirm node.
# ls -al /dev/openfirm crw------- 1 root wheel 0, 24 Jan 1 00:00 /dev/openfirm
# ofwdump -a
Node 0xc06309a0:
Node 0xc0630a04: aliases
Node 0xc0630b04: cpus
Node 0xc0630b30: PowerPC,8555@0
Node 0xc0630bec: memory
Node 0xc0630c24: soc8555@e0000000
Node 0xc0630cac: ecm-law@0
Node 0xc0630cfc: ecm@1000
Node 0xc0630d6c: memory-controller@2000
Node 0xc0630dec: l2-cache-controller@20000
Node 0xc0630ea4: i2c@3000
Node 0xc0630f40: dma@21300
Node 0xc0630fd8: dma-channel@0
Node 0xc0631074: dma-channel@80
Node 0xc0631110: dma-channel@100
Node 0xc06311ac: dma-channel@180
Node 0xc063124c: ethernet@24000
Node 0xc0631360: mdio@520
Node 0xc06313c4: ethernet-phy@0
Node 0xc063143c: ethernet-phy@1
Node 0xc06314b4: tbi-phy@11
Node 0xc0631504: ethernet@25000
Node 0xc0631618: mdio@520
Node 0xc0631678: tbi-phy@11
Node 0xc06316c8: serial@4500
Node 0xc063175c: serial@4600
Node 0xc06317f0: crypto@30000
Node 0xc0631898: pic@40000
Node 0xc0631930: cpm@919c0
Node 0xc06319a8: muram@80000
Node 0xc06319f0: data@0
Node 0xc0631a40: brg@919f0
Node 0xc0631aa8: pic@90c00
Node 0xc0631b58: pci@e0008000
Node 0xc0631fa8: i8259@19000
Node 0xc0632068: pci@e0009000
#
# ofwdump -p /soc8555
Node 0xc0630c24: soc8555@e0000000
#address-cells:
00 00 00 01
#size-cells:
00 00 00 01
device_type:
73 6f 63 00
'soc'
compatible:
73 69 6d 70 6c 65 2d 62 75 73 00
'simple-bus'
ranges:
00 00 00 00 e0 00 00 00 00 10 00 00
bus-frequency:
00 00 00 00
#
# ofwdump -p /soc8555/ethernet@24000
Node 0xc063124c: ethernet@24000
#address-cells:
00 00 00 01
#size-cells:
00 00 00 01
cell-index:
00 00 00 00
device_type:
6e 65 74 77 6f 72 6b 00
'network'
model:
54 53 45 43 00
'TSEC'
compatible:
67 69 61 6e 66 61 72 00
'gianfar'
reg:
00 02 40 00 00 00 10 00
ranges:
00 00 00 00 00 02 40 00 00 00 10 00
local-mac-address:
00 00 00 00 00 00
interrupts:
00 00 00 1d 00 00 00 02 00 00 00 1e 00 00 00 02 00 00 00 22
00 00 00 02
interrupt-parent:
00 00 00 02
tbi-handle:
00 00 00 03
phy-handle:
00 00 00 04
#
There is another option in case you want to see the DTS you booted with:
# sysctl -b hw.fdt.dtb | dtc -I dtb
If the dtc has to be (re)built for whatever reason manually, it can be achieved similar to any other user land applications of the base FreeBSD system. In this case:
$ cd gnu/usr.bin/dtc/ $ make ...
$ ./dtc -v Version: DTC 1.2.0-g46b8d261
$ ./dtc -h Usage: dtc [options] <input file> Options: -h This help text -q Quiet: -q suppress warnings, -qq errors, -qqq all -I <input format> Input formats are: dts - device tree source text dtb - device tree blob fs - /proc/device-tree style directory -o <output file> -O <output format> Output formats are: dts - device tree source text dtb - device tree blob asm - assembler source -V <output version> Blob version to produce, defaults to 17 (relevant for dtb and asm output only) -R <number> Make space for <number> reserve map entries (relevant for dtb and asm output only) -S <bytes> Make the blob at least <bytes> long (extra space) -p <bytes> Add padding to the blob of <bytes> long (extra space) -b <number> Set the physical boot cpu -f Force - try to produce output even if the input tree has errors -v Print DTC version and exit
$ ./dtc -O dtb -o mpc8555cds.dtb -b 0 -p 1024 mpc8555cds.dts DTC: dts->dtb on file "mpc8555cds.dts" $ file mpc8555cds.dtb mpc8555cds.dtb: data
$ hexdump -C mpc8555cds.dtb 00000000 d0 0d fe ed 00 00 1e b2 00 00 00 38 00 00 18 ac |...........8....| 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............| 00000020 00 00 02 06 00 00 18 74 00 00 00 00 00 00 00 00 |.......t........| 00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00000040 00 00 00 03 00 00 00 0b 00 00 00 00 4d 50 43 38 |............MPC8| 00000050 35 35 35 43 44 53 00 00 00 00 00 03 00 00 00 16 |555CDS..........| 00000060 00 00 00 06 4d 50 43 38 35 35 35 43 44 53 00 4d |....MPC8555CDS.M| 00000070 50 43 38 35 78 78 43 44 53 00 00 00 00 00 00 03 |PC85xxCDS.......| 00000080 00 00 00 04 00 00 00 11 00 00 00 01 00 00 00 03 |................| 00000090 00 00 00 04 00 00 00 20 00 00 00 01 00 00 00 01 |....... ........| 000000a0 61 6c 69 61 73 65 73 00 00 00 00 03 00 00 00 21 |aliases........!| ...