devicetree.bbclass 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #
  2. # Copyright OpenEmbedded Contributors
  3. #
  4. # SPDX-License-Identifier: MIT
  5. #
  6. # This bbclass implements device tree compilation for user provided device tree
  7. # sources. The compilation of the device tree sources is the same as the kernel
  8. # device tree compilation process, this includes being able to include sources
  9. # from the kernel such as soc dtsi files or header files such as gpio.h. In
  10. # addition to device trees this bbclass also handles compilation of device tree
  11. # overlays.
  12. #
  13. # The output of this class behaves similar to how kernel-devicetree.bbclass
  14. # operates in that the output files are installed into /boot/devicetree.
  15. # However this class on purpose separates the deployed device trees into the
  16. # 'devicetree' subdirectory. This prevents clashes with the kernel-devicetree
  17. # output. Additionally the device trees are populated into the sysroot for
  18. # access via the sysroot from within other recipes.
  19. SECTION ?= "bsp"
  20. # The default inclusion of kernel device tree includes and headers means that
  21. # device trees built with them are at least GPL-2.0-only (and in some cases dual
  22. # licensed). Default to GPL-2.0-only if the recipe does not specify a license.
  23. LICENSE ?= "GPL-2.0-only"
  24. LIC_FILES_CHKSUM ?= "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171dd6425610833a22dbe6"
  25. INHIBIT_DEFAULT_DEPS = "1"
  26. DEPENDS += "dtc-native"
  27. inherit deploy kernel-arch
  28. COMPATIBLE_MACHINE ?= "^$"
  29. PROVIDES = "virtual/dtb"
  30. PACKAGE_ARCH = "${MACHINE_ARCH}"
  31. SYSROOT_DIRS += "/boot/devicetree"
  32. FILES:${PN} = "/boot/devicetree/*.dtb /boot/devicetree/*.dtbo"
  33. S = "${UNPACKDIR}"
  34. B = "${WORKDIR}/build"
  35. # Default kernel includes, these represent what are normally used for in-kernel
  36. # sources.
  37. KERNEL_INCLUDE ??= " \
  38. ${STAGING_KERNEL_DIR}/arch/${ARCH}/boot/dts \
  39. ${STAGING_KERNEL_DIR}/arch/${ARCH}/boot/dts/* \
  40. ${STAGING_KERNEL_DIR}/scripts/dtc/include-prefixes \
  41. "
  42. DT_INCLUDE[doc] = "Search paths to be made available to both the device tree compiler and preprocessor for inclusion."
  43. DT_INCLUDE ?= "${DT_FILES_PATH} ${KERNEL_INCLUDE}"
  44. DT_FILES_PATH[doc] = "Path to the directory containing dts files to build. Defaults to source directory."
  45. DT_FILES_PATH ?= "${S}"
  46. DT_FILES[doc] = "Space-separated list of dts or dtb files (relative to DT_FILES_PATH) to build. If empty, all dts files are built."
  47. DT_FILES ?= ""
  48. DT_PADDING_SIZE[doc] = "Size of padding on the device tree blob, used as extra space typically for additional properties during boot."
  49. DT_PADDING_SIZE ??= "0x3000"
  50. DT_RESERVED_MAP[doc] = "Number of reserved map entires."
  51. DT_RESERVED_MAP ??= "8"
  52. DT_BOOT_CPU[doc] = "The boot cpu, defaults to 0"
  53. DT_BOOT_CPU ??= "0"
  54. DTC_FLAGS ?= "-R ${DT_RESERVED_MAP} -b ${DT_BOOT_CPU}"
  55. DTC_PPFLAGS ?= "-nostdinc -undef -D__DTS__ -x assembler-with-cpp"
  56. DTC_BFLAGS ?= "-p ${DT_PADDING_SIZE} -@"
  57. DTC_OFLAGS ?= "-p 0 -@ -H epapr"
  58. python () {
  59. if d.getVar("KERNEL_INCLUDE"):
  60. # auto add dependency on kernel tree, but only if kernel include paths
  61. # are specified.
  62. d.appendVarFlag("do_compile", "depends", " virtual/kernel:do_configure")
  63. }
  64. def expand_includes(varname, d):
  65. import glob
  66. includes = set()
  67. # expand all includes with glob
  68. for i in (d.getVar(varname) or "").split():
  69. for g in glob.glob(i):
  70. if os.path.isdir(g): # only add directories to include path
  71. includes.add(g)
  72. return includes
  73. def devicetree_source_is_overlay(path):
  74. # determine if a dts file is an overlay by checking if it uses "/plugin/;"
  75. with open(path, "r") as f:
  76. for i in f:
  77. if i.startswith("/plugin/;"):
  78. return True
  79. return False
  80. def devicetree_compile(dtspath, includes, d):
  81. import subprocess
  82. dts = os.path.basename(dtspath)
  83. dtname = os.path.splitext(dts)[0]
  84. bb.note("Processing {0} [{1}]".format(dtname, dts))
  85. # preprocess
  86. ppargs = d.getVar("BUILD_CPP").split()
  87. ppargs += (d.getVar("DTC_PPFLAGS") or "").split()
  88. for i in includes:
  89. ppargs.append("-I{0}".format(i))
  90. ppargs += ["-o", "{0}.pp".format(dts), dtspath]
  91. bb.note("Running {0}".format(" ".join(ppargs)))
  92. try:
  93. subprocess.run(ppargs, check=True, capture_output=True)
  94. except subprocess.CalledProcessError as e:
  95. bb.fatal(f"Command '{' '.join(ppargs)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\ndtspath: {os.path.abspath(dtspath)}")
  96. # determine if the file is an overlay or not (using the preprocessed file)
  97. isoverlay = devicetree_source_is_overlay("{0}.pp".format(dts))
  98. # compile
  99. dtcargs = ["dtc"] + (d.getVar("DTC_FLAGS") or "").split()
  100. if isoverlay:
  101. dtcargs += (d.getVar("DTC_OFLAGS") or "").split()
  102. else:
  103. dtcargs += (d.getVar("DTC_BFLAGS") or "").split()
  104. for i in includes:
  105. dtcargs += ["-i", i]
  106. dtcargs += ["-o", "{0}.{1}".format(dtname, "dtbo" if isoverlay else "dtb")]
  107. dtcargs += ["-I", "dts", "-O", "dtb", "{0}.pp".format(dts)]
  108. bb.note("Running {0}".format(" ".join(dtcargs)))
  109. try:
  110. subprocess.run(dtcargs, check=True, capture_output=True)
  111. except subprocess.CalledProcessError as e:
  112. bb.fatal(f"Command '{' '.join(dtcargs)}' failed with return code {e.returncode}\nstdout: {e.stdout.decode()}\nstderr: {e.stderr.decode()}\ndtname: {dtname}")
  113. python devicetree_do_compile() {
  114. import re
  115. includes = expand_includes("DT_INCLUDE", d)
  116. dtfiles = d.getVar("DT_FILES").split()
  117. dtfiles = [ re.sub(r"\.dtbo?$", ".dts", dtfile) for dtfile in dtfiles ]
  118. listpath = d.getVar("DT_FILES_PATH")
  119. for dts in dtfiles or os.listdir(listpath):
  120. dtspath = os.path.join(listpath, dts)
  121. try:
  122. if not(os.path.isfile(dtspath)) or not(dts.endswith(".dts") or devicetree_source_is_overlay(dtspath)):
  123. continue # skip non-.dts files and non-overlay files
  124. except:
  125. continue # skip if can't determine if overlay
  126. devicetree_compile(dtspath, includes, d)
  127. }
  128. devicetree_do_install() {
  129. for dtb_file in *.dtb *.dtbo; do
  130. [ -e "$dtb_file" ] || continue
  131. install -Dm 0644 "${B}/$dtb_file" "${D}/boot/devicetree/$dtb_file"
  132. done
  133. }
  134. devicetree_do_deploy() {
  135. for dtb_file in *.dtb *.dtbo; do
  136. [ -e "$dtb_file" ] || continue
  137. install -Dm 0644 "${B}/$dtb_file" "${DEPLOYDIR}/devicetree/$dtb_file"
  138. done
  139. }
  140. addtask deploy before do_build after do_install
  141. EXPORT_FUNCTIONS do_compile do_install do_deploy