devicetree: kernel internals and practical troubleshooting There have been many presentations on what a devicetree looks like and how to create a devicetree. This talk instead examines how the Linux kernel uses a devicetree. Topics include the kernel devicetree framework, device creation, resource allocation, driver binding, and connecting objects. Troubleshooting will consider initialization, allocation, and binding ordering; kernel configuration; and driver problems. Frank Rowand, Sony Mobile Communications August 22, 2014 140821_1927
CAUTION The material covered in this presentation is kernel version specific Most information describes 3.16 or earlier In cases where arch specific code is involved, there will be a bias to looking at arch/arm/
Chapter 1 Device tree
what is device tree? “A device tree is a tree data structure with nodes that describe the devices in a system. Each node has property/value pairs that describe the characteristics of the device being represented. Each node has exactly one parent except for the root node, which has no parent.” (ePAPR v1.1)
what is device tree? “A device tree is a tree data structure with nodes that describe the devices in a system. Each node has property/value pairs that describe the characteristics of the device being represented. Each node has exactly one parent except for the root node, which has no parent.” (ePAPR v1.1) A device tree describes hardware that can not be located by probing.
DT data life cycle (source) .dts
.dts - device tree source file / { /* incomplete .dts example */ model = "Qualcomm APQ8074 Dragonboard"; compatible = "qcom,apq8074-dragonboard"; interrupt-parent = <&intc>; soc: soc { ranges; compatible = "simple-bus"; intc: interrupt-controller@f9000000 { compatible = "qcom,msm-qgic2"; interrupt-controller; reg = <0xf9000000 0x1000>, <0xf9002000 0x1000>; console: serial@f991e000 { compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; reg = <0xf991e000 0x1000>; interrupts = <0 108 0x0>; };
.dts - device tree source file Thomas Pettazzoni's ELC 2014 talk “Device Tree For Dummies” is an excellent introduction to - device tree source - boot loader mechanisms - much more! http://elinux.org/images/f/f9/ Petazzoni-device-tree-dummies_0.pdf
.dts - device tree source file / { /* incomplete .dts example */ <--- root node model = "Qualcomm APQ8074 Dragonboard"; <--- property compatible = "qcom,apq8074-dragonboard"; <--- property interrupt-parent = <&intc>; <--- property, phandle soc: soc { <--- node ranges; <--- property compatible = "simple-bus"; <--- property intc: interrupt-controller@f9000000 { <--- node, phandle compatible = "qcom,msm-qgic2"; <--- property interrupt-controller; <--- property reg = <0xf9000000 0x1000>, <--- property <0xf9002000 0x1000>; serial@f991e000 { <--- node compatible = "qcom,msm-uartdm-v1.4", "qcom,msm-uartdm"; reg = <0xf991e000 0x1000>; <--- property interrupts = <0 108 0x0>; <--- property }; }; };
Key vocabulary nodes - the tree structure - contain properties and other nodes properties - data values providing information about a node node '/': property 'compatible' - will be used to match a machine_desc entry other nodes: property 'compatible' - will be used to match a driver
DT data life cycle (source) (compiler) (binary blob) .dts dtc .dtb
DT data life cycle (source) (compiler) (binary blob) .dts dtc .dtb
Binary Blob format A “flat” format Access via serial scan and offsets
Binary Blob format info struct fdt_header offsets to blocks section sizes (free space) memory reservation block {address, size} tuples (free space) nested nodes structure block - name embedded properties nested in nodes - values embedded - names are offsets in 'strings' (free space) property names strings block - null terminated strings - concatenated (free space)
DT data life cycle (source) (compiler) (binary blob) .dts dtc .dtb boot dtb boot vmlinux loader: image: dtb' dtb FDT memory: (flattened device tree)
DT data life cycle (source) (compiler) (binary blob) .dts dtc .dtb boot dtb boot vmlinux loader: image: dtb' dtb FDT memory: (flattened device linux tree) kernel
Flattened Device Tree format A “flat” format. Access via serial scan and offsets using fdt_*() functions.
DT data life cycle (source) (compiler) (binary blob) .dts dtc .dtb boot dtb boot vmlinux loader: image: dtb' dtb FDT memory: (flattened device linux tree) kernel expanded DT
Expanded format A “tree” data structure Access and modified via tree operations using of_*() functions Access all nodes via a linked list Created during boot Nodes and properties can be added or deleted after boot
Expanded format tree of struct device_node struct device_node { const char *name; const char *type; phandle phandle; const char *full_name; struct property *properties; struct property *deadprops; struct device_node *parent; struct device_node *child; struct device_node *sibling; struct device_node *next; struct device_node *allnext; struct kobject kobj; unsigned long _flags; void *data; #if defined(CONFIG_SPARC) ... #endif };
Expanded format tree of struct device_node struct device_node { struct property *properties; struct device_node *parent; struct device_node *child; struct device_node *sibling; struct device_node *allnext; };
Expanded format tree of struct device_node Tree pointers child pointer sibling pointer
Expanded format tree of struct device_node of_allnodes child sibling
Expanded format tree of struct device_node Tree pointers child pointer sibling pointer Used to find node by tree search of_find_node_by_path()
Expanded format tree of struct device_node Global linked list pointer allnext pointer
Expanded format tree of struct device_node child sibling allnext
allnext linked list Follows a depth first traversal of the tree After boot: YES After dynamic node addition: NO To be safe, think of allnext as a randomly ordered linked list of all nodes.
allnext linked list - internal usage Common pattern: of_find_by_XXX(struct device node *from, …) { np = from ? from->allnext : of_allnodes; for (; np; np = np->allnext) … If 'from' is NULL then search from of_allnodes, else search from a specific starting point
allnext linked list - internal usage Find nodes by attribute struct device_node { const char *name; const char *type; phandle phandle; const char *full_name;
allnext linked list - internal usage Find nodes by attribute of_find_node_by_name (*from, …) of_find_node_by_type (*from, …) of_find_node_by_phandle (handle) handle is unique, so *from not needed of_find_node_with_property (*from, …) traverse allnext and properties
allnext linked list Properties 'name' and 'device_type' are special. memory { device_type = "memory"; reg = <0 0>; }; In addition to existing on the node's properties list, these properties are hoisted into: device_node.name device_node.type
Expanded format tree of struct device_node parent pointer
Expanded format tree of struct device_node parent child sibling allnext
Expanded format tree of struct device_node properties pointer struct property { char *name; int length; void *value; struct property *next; unsigned long _flags; unsigned int unique_id; struct bin_attribute attr; };
Expanded format tree of struct device_node child sibling properties next
Chapter 2 Matching boot customization options to the device tree Kernel boot
Linux kernel - machine_desc Boot customizations for different device trees
machine_desc struct machine_desc { unsigned int nr; /* architecture */ const char *name; /* architecture */ unsigned long atag_offset; char *dt_compat; /* 'compatible' strings */ unsigned int nr_irqs; phys_addr_t dma_zone_size; ... enum reboot_mode reboot_mode; unsigned l2c_aux_val; /* L2 cache */ unsigned l2c_aux_mask; /* L2 cache */ ... struct smp_operations *smp; bool (*XXX_init)(); bool (*YYY_init)(); ...
machine_desc - populating #define DT_MACHINE_START(_name, _namestr) \ struct machine_desc __mach_desc_##_name \ __section__(".arch.info.init" = { \ .nr = ~0, \ .name = _namestr, #define MACHINE_END }; * Essential features extracted, actual code is on next slide
Recommend
More recommend