diff --git a/ci/capsule.yml b/ci/capsule.yml index cc8d240e0..ef9d57e65 100644 --- a/ci/capsule.yml +++ b/ci/capsule.yml @@ -10,6 +10,18 @@ header: version: 14 +# meta-oe provides the `openembedded-layer` collection name, which gates +# our local python3-pyfdt and qdte recipes under +# dynamic-layers/openembedded-layer/ (see conf/layer.conf BBFILES_DYNAMIC). +# meta-python is not required: pyfdt is shipped via the local recipe, +# and qdte's other runtime deps (fs, pyfatfs, tkinter) are stubbed by +# recipe-local patches when running --nogui. +repos: + meta-openembedded: + url: https://github.com/openembedded/meta-openembedded + layers: + meta-oe: + local_conf_header: capsule: | PREFERRED_PROVIDER_virtual/qcom-capsule-firmware = "firmware-qcom-capsule" diff --git a/classes-recipe/qcom-capsule.bbclass b/classes-recipe/qcom-capsule.bbclass index dcbe57ca5..4a93153c7 100644 --- a/classes-recipe/qcom-capsule.bbclass +++ b/classes-recipe/qcom-capsule.bbclass @@ -36,19 +36,12 @@ CAPSULE_SUB_PUB ?= "" # --------------------------------------------------------------------------- # XBLConfig DTB certificate injection # --------------------------------------------------------------------------- -# The class automatically detects the post-DDR DTB by parsing the output of -# xblconfig_parser.py dump (looks for the first entry matching post-ddr*.dtb). -# Both the filename and the section index are extracted from the dump output. -# -# XBLCONFIG_DTB overrides auto-detection when set to an explicit filename. -# XBLCONFIG_DTB_SECTION overrides the auto-detected section index. -# -# When a post-DDR DTB is found (auto or explicit), the class will: -# 1. dump XBLConfig sections -# 2. patch QcCapsuleRootCert in the DTB with the converted root cert -# 3. re-pack the updated DTB back into xbl_config.elf -XBLCONFIG_DTB ?= "" -XBLCONFIG_DTB_SECTION ?= "" +# The class invokes QDTE (qdte --nogui) to disassemble xbl_config.elf, +# patch QcCapsuleRootCert in the named DTB, and reassemble in one shot. +# QDTE does not auto-detect which DTB inside xbl_config.elf carries the +# property, so XBLCONFIG_DTB must be set to the filename of the post-DDR +# DTB (e.g. "post-ddr-kodiak-1.0.dtb"). +XBLCONFIG_DTB ?= "" # --------------------------------------------------------------------------- # Boot binaries location @@ -89,7 +82,8 @@ inherit python3native deploy CAPSULE_DIR = "${WORKDIR}/capsule_gen" do_compile[depends] += "cbsp-boot-utilities-native:do_populate_sysroot \ - edk2-basetools-native:do_populate_sysroot" + edk2-basetools-native:do_populate_sysroot \ + qdte-native:do_populate_sysroot" do_compile[dirs] = "${CAPSULE_DIR}" do_compile[cleandirs] = "${CAPSULE_DIR}" @@ -203,55 +197,61 @@ python generate_fvupdate() { do_compile[prefuncs] += "generate_fvupdate" -# Inject the OEM root certificate into xbl_config.elf. -# Dumps the config sections, auto-detects the post-DDR DTB (or uses -# XBLCONFIG_DTB / XBLCONFIG_DTB_SECTION overrides), patches QcCapsuleRootCert -# in that DTB, and repacks the updated DTB back into xbl_config.elf in place. +# Inject the OEM root certificate into xbl_config.elf using QDTE. +# +# QDTE's --nogui mode disassembles xbl_config.elf into its constituent +# DTBs, applies an EDIT_PROPERTY_VALUE op via --modify, then reassembles. +# That collapses the three previous cbsp-boot-utilities steps (dump, +# set-dtb-property, replace) into a single invocation -- but QDTE does +# not auto-detect which DTB inside xbl_config.elf to patch, so the +# integrator must set XBLCONFIG_DTB (e.g. "post-ddr-kodiak-1.0.dtb"). +# +# The DER cert is converted directly into QDTE's --modify value syntax +# via python3 (staged through python3-native, in PATH); cbsp-boot-utilities' +# bin-to-hex is not on this path. +# # $1 - path to xbl_config.elf (modified in place on success) patch_xblconfig_cert() { local xbl_config="$1" local staged_dir staged_dir=$(dirname "${xbl_config}") - XBL_DUMP_LOG="${CAPSULE_DIR}/xbl_dump.log" - qcom-capsule-tool parse-config \ - "${xbl_config}" dump \ - --out-dir "${staged_dir}" | tee "${XBL_DUMP_LOG}" - - DTB_PATCH="${XBLCONFIG_DTB}" - DTB_SECTION="${XBLCONFIG_DTB_SECTION}" - if [ -z "${DTB_PATCH}" ]; then - # Parse a line like: - # [+] config_item[6] -> PH# 8 -> './post-ddr-kodiak-1.0.dtb' (90280 bytes) - POST_DDR_LINE=$(grep -m1 "post-ddr.*\.dtb" "${XBL_DUMP_LOG}" || true) - if [ -n "${POST_DDR_LINE}" ]; then - DTB_PATCH=$(echo "${POST_DDR_LINE}" | sed "s|.* -> '||;s|'.*||" | xargs basename) - DTB_SECTION=$(echo "${POST_DDR_LINE}" | sed "s/.*PH# \([0-9]*\).*/\1/") - fi + if [ -z "${XBLCONFIG_DTB}" ]; then + bbfatal "XBLCONFIG_DTB must be set when using QDTE for cert injection." fi - if [ -n "${DTB_PATCH}" ]; then - ORIG_DTB="${staged_dir}/${DTB_PATCH}" - UPDATED_DTB="${staged_dir}/${DTB_PATCH%.dtb}-updated.dtb" - - qcom-capsule-tool set-dtb-property \ - "${ORIG_DTB}" \ - /sw/uefi/uefiplat \ - QcCapsuleRootCert \ - "@list:${ROOT_INC}" \ - "${UPDATED_DTB}" - - qcom-capsule-tool parse-config \ - "${xbl_config}" replace \ - "${DTB_SECTION}" \ - "${UPDATED_DTB}" \ - "${staged_dir}/xbl_config_patched.elf" - - mv "${staged_dir}/xbl_config_patched.elf" \ - "${xbl_config}" - - touch "${CAPSULE_DIR}/.xbl_with_oem_cert" - fi + # QcCapsuleRootCert is stored in the DTB as FdtPropertyWords: a + # length-prefixed sequence of 32-bit big-endian unsigned integers. + # Verified via fdtdump on a reference post-DDR DTB: + # + # QcCapsuleRootCert = <0x000003a6 0x308203a2 0x3082028a ... >; + # + # Word 0 is the cert length in bytes; subsequent words pack the DER + # cert four bytes at a time, big-endian, zero-padded to a multiple + # of 4 bytes. QDTE's --modify splits on ';' and feeds each token to + # the property setter; word-typed properties expect uint32 hex + # literals (matching the existing property type). + QDTE_CERT_VALUE=$(python3 -c ' +import struct, sys +data = open(sys.argv[1], "rb").read() +pad = (-len(data)) % 4 +padded = data + b"\x00" * pad +words = [len(data)] + list(struct.unpack(">%dI" % (len(padded) // 4), padded)) +print(";".join("0x%08x" % w for w in words)) +' "${CAPSULE_ROOT_CER}") + + local qdte_outdir="${CAPSULE_DIR}/qdte_out" + mkdir -p "${qdte_outdir}" + + qdte --nogui \ + --allow_unsigned \ + --input_file "${xbl_config}" \ + --output_path "${qdte_outdir}" \ + --output_file "xbl_config.elf" \ + --modify "${XBLCONFIG_DTB}/sw/uefi/uefiplat/QcCapsuleRootCert=${QDTE_CERT_VALUE}" + + install -m 0644 "${qdte_outdir}/xbl_config.elf" "${xbl_config}" + touch "${CAPSULE_DIR}/.xbl_with_oem_cert" } do_compile() { @@ -279,9 +279,6 @@ do_compile() { cd "${CAPSULE_DIR}" - ROOT_INC="${CAPSULE_DIR}/QcFMPRoot.inc" - qcom-capsule-tool bin-to-hex "${CAPSULE_ROOT_CER}" "${ROOT_INC}" - # Stage boot binaries so they are writable (XBLConfig patching modifies # xbl_config.elf in place) BOOTBINS_STAGED="${CAPSULE_DIR}/bootbins" diff --git a/conf/machine/include/qcom-qcs6490.inc b/conf/machine/include/qcom-qcs6490.inc index 95ff4660b..20ec7c205 100644 --- a/conf/machine/include/qcom-qcs6490.inc +++ b/conf/machine/include/qcom-qcs6490.inc @@ -15,3 +15,8 @@ MACHINE_ESSENTIAL_EXTRA_RRECOMMENDS += " \ MACHINE_EXTRA_RRECOMMENDS += " \ packagegroup-qcom-boot-additional \ " + +# Post-DDR DTB embedded inside xbl_config.elf for QCM6490 (Kodiak). +# Used by qcom-capsule.bbclass to locate the DTB into which the OEM +# capsule root certificate is injected at build time. +XBLCONFIG_DTB ?= "post-ddr-kodiak-1.0.dtb" diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/python/python3-pyfdt_0.3.bb b/dynamic-layers/openembedded-layer/recipes-devtools/python/python3-pyfdt_0.3.bb new file mode 100644 index 000000000..f2e1d34bc --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/python/python3-pyfdt_0.3.bb @@ -0,0 +1,16 @@ +SUMMARY = "Python Flattened Device Tree library" +DESCRIPTION = "Pure-Python library for parsing and writing Flattened \ +Device Tree (FDT) blobs. Required by QDTE (qdte-native) for headless \ +xbl_config.elf DTB manipulation in the UEFI capsule build pipeline." +HOMEPAGE = "https://github.com/superna9999/pyfdt" +LICENSE = "Apache-2.0" + +# The 0.3 sdist on PyPI ships no LICENSE file, so anchor the checksum +# to the Apache-2.0 header embedded at the top of pyfdt/pyfdt.py. +LIC_FILES_CHKSUM = "file://pyfdt/pyfdt.py;beginline=4;endline=18;md5=727e7a76c771b92141ef85ee99d820ff" + +SRC_URI[sha256sum] = "61601c2005ff394a25a6c84c6da2088bbf888328038400d27e4eeb1b04b9f4f0" + +inherit pypi setuptools3 + +BBCLASSEXTEND = "native nativesdk" diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0001-controller-make-non_hlos_parser-import-optional.patch b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0001-controller-make-non_hlos_parser-import-optional.patch new file mode 100644 index 000000000..e54648b9d --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0001-controller-make-non_hlos_parser-import-optional.patch @@ -0,0 +1,58 @@ +From 17e451d07c84718be4c48fe5b2b3a7fce4797a17 Mon Sep 17 00:00:00 2001 +From: Igor Opaniuk +Date: Sun, 14 Jun 2026 20:54:26 +0200 +Subject: [PATCH] controller: make non_hlos_parser import optional + +The NON-HLOS.bin parser uses PyFilesystem2 (fs) and pyfatfs to walk a +FAT-formatted multi-firmware image. This is functionality only +reachable via the Tk GUI -- specifically the DTGUIController class +which inherits non_hlos_parser.nhlos_Operator and is only instantiated +in the GUI branch of run(). + +When QDTE is used headless (--nogui) for tasks like patching a single +DTB property inside xbl_config.elf, requiring fs / pyfatfs to be +installed is unnecessary friction. In environments such as +OpenEmbedded native sysroots these packages are not always packaged. + +Make the top-level import conditional on its availability and provide +a stub class so the DTGUIController class definition still parses. +The stub is never instantiated (controller.run only calls +DTGUIController(...) in the non-nogui branch) so the GUI behaviour is +unchanged. When fs / pyfatfs are present the real module loads as +before. + +Upstream-Status: Submitted [https://github.com/qualcomm/DTE/pull/N] +Signed-off-by: Igor Opaniuk +--- + controller.py | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/controller.py b/controller.py +index 6c836b9..add04c3 100644 +--- a/controller.py ++++ b/controller.py +@@ -66,7 +66,20 @@ from pyfdt import pyfdt + import Autocmd as cmd + + #nhlos parser lib +-import non_hlos_parser ++# ++# The NON-HLOS.bin parser is only used by the GUI's FAT image browser ++# (DTGUIController instantiated via the `else` branch in run()). Its ++# transitive deps (`fs`, `pyfatfs`) are unnecessary for headless ++# --nogui usage such as xbl_config.elf modification, so import lazily ++# and fall back to a stub when those packages are not installed. The ++# stub satisfies the DTGUIController class-definition contract without ++# ever being instantiated under --nogui. ++try: ++ import non_hlos_parser ++except ImportError: ++ class non_hlos_parser: # type: ignore ++ class nhlos_Operator: ++ pass + + import get_qsahara_files + +-- +2.53.0 + diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0002-run-stub-tkinter-for-nogui-mode.patch b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0002-run-stub-tkinter-for-nogui-mode.patch new file mode 100644 index 000000000..cd4bf3260 --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0002-run-stub-tkinter-for-nogui-mode.patch @@ -0,0 +1,99 @@ +From f7938c07dca4712f16f88435577caf02cd800649 Mon Sep 17 00:00:00 2001 +From: Igor Opaniuk +Date: Sun, 14 Jun 2026 21:05:31 +0200 +Subject: [PATCH] run: stub tkinter for --nogui mode + +QDTE's modules import tkinter at top level pervasively (controller.py, +treeview.py, settings.py, hexview.py, xblcfgint.py, and several +others), and define GUI classes like `class DTGUIController(tk.Frame, ...)` +whose bases are looked up at class-definition time. As a result the +mere act of importing controller -- which run.py does +unconditionally -- requires Tcl/Tk to be available in the host +environment, even when the user only wants the headless --nogui mode +for tasks like patching a single property inside xbl_config.elf. + +In minimal Yocto native sysroots, Qualcomm-internal CI containers, and +similar restricted environments, tkinter is not always packaged. The +result is a hard ImportError before any headless logic has a chance +to run. + +Install a permissive stub `tkinter` module (plus submodules: font, +ttk, colorchooser, messagebox, filedialog, simpledialog, +scrolledtext) into sys.modules at the very top of run.py when +--nogui is present in argv. The stub is: + + * subclassable, so `class Foo(tk.Frame)` succeeds during + class-body execution; + * callable, so `tk.Tk()` evaluates without raising (the run_str + expression at the bottom of __main__ short-circuits via + `None if nogui else tk.Tk()`, but the lookup itself still + has to succeed); + * attribute-polymorphic via __getattr__ on both the stub class + and the stub module (PEP 562), so `from tkinter import E / + W / N / ttk / font / ...` all resolve to the stub class. + +The stubs are never instantiated under --nogui because the GUI code +paths (DTGUIController, TreeView, etc.) are only constructed in the +non-nogui branch of controller.run(). When tkinter is present and +the GUI is used, the stub installation is skipped entirely and +behaviour is unchanged. + +Upstream-Status: Submitted [https://github.com/qualcomm/DTE/pull/N] +Signed-off-by: Igor Opaniuk +--- + run.py | 39 +++++++++++++++++++++++++++++++++++++++ + 1 file changed, 39 insertions(+) + +diff --git a/run.py b/run.py +index fb3767d..b35d4ce 100644 +--- a/run.py ++++ b/run.py +@@ -14,6 +14,45 @@ An example of when this file could come in use is when profiling application per + decide upon an appropriate tool to use and set up the output profile file, etc. + """ + import sys ++ ++# When invoked in --nogui mode, QDTE never instantiates any tkinter ++# widgets -- the controller.run() nogui branch only uses DTWrapper and ++# Autocmd. Install a permissive stub for `tkinter` (and its submodules) ++# before anything else loads so that the pervasive top-level ++# `import tkinter` / `from tkinter import E` / `class X(tk.Frame)` ++# statements throughout QDTE succeed in environments where Tcl/Tk is ++# not available, such as minimal Yocto native sysroots. ++if '--nogui' in sys.argv: ++ import types ++ ++ class _TkStub: ++ """Pretends to be any tkinter symbol -- class, constant, callable. ++ Subclassable (so `class Foo(tk.Frame)` works), callable (so ++ `tk.Tk()` works if it ever runs), attribute-polymorphic.""" ++ def __init__(self, *args, **kwargs): ++ pass ++ def __getattr__(self, name): ++ return _TkStub ++ def __call__(self, *args, **kwargs): ++ return _TkStub() ++ def __class_getitem__(cls, item): ++ return _TkStub ++ ++ def _make_stub_module(name): ++ mod = types.ModuleType(name) ++ mod.__path__ = [] # mark as package so submodule imports resolve ++ # PEP 562: __getattr__ on a module handles `from import X` ++ # for unknown names by returning the stub class. ++ mod.__getattr__ = lambda n: _TkStub ++ return mod ++ ++ sys.modules['tkinter'] = _make_stub_module('tkinter') ++ for _sub in ('font', 'ttk', 'colorchooser', 'messagebox', ++ 'filedialog', 'simpledialog', 'scrolledtext'): ++ _stub = _make_stub_module('tkinter.' + _sub) ++ sys.modules['tkinter.' + _sub] = _stub ++ setattr(sys.modules['tkinter'], _sub, _stub) ++ + import argparse + import tkinter as tk + import flags as gflags +-- +2.53.0 + diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0003-controller-guard-quts2-lookup-on-Linux.patch b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0003-controller-guard-quts2-lookup-on-Linux.patch new file mode 100644 index 000000000..5870fba09 --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0003-controller-guard-quts2-lookup-on-Linux.patch @@ -0,0 +1,43 @@ +From 3da82bee62fe3999968b0c3935a33d23b6149947 Mon Sep 17 00:00:00 2001 +From: Igor Opaniuk +Date: Sun, 14 Jun 2026 21:13:43 +0200 +Subject: [PATCH] controller: guard quts2 lookup on Linux + +flags.py:global_info defines a "quts2" key only in its Windows branch +(it points at the secondary 64-bit "Program Files\Qualcomm\QUTS" +install path). On Linux, only "quts" is present, so the QUTS startup +probe in controller.py raises KeyError on the elif lookup before any +QDTE logic gets a chance to run. + +Guard the second lookup with a containment check so the Linux path +falls through cleanly. The QUTS toolchain is optional anyway -- the +surrounding block silently ignores missing files via the +os.path.exists chain that follows. + +Upstream-Status: Submitted [https://github.com/qualcomm/DTE/pull/N] +Signed-off-by: Igor Opaniuk +--- + controller.py | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/controller.py b/controller.py +index add04c3..e4c9850 100644 +--- a/controller.py ++++ b/controller.py +@@ -86,9 +86,12 @@ import get_qsahara_files + + QUTS_STATE = None + quts_path = None ++# The "quts2" key is only present in the Windows branch of ++# flags.py:global_info (see the platform check there). Guard the ++# lookup so the Linux startup path does not raise KeyError. + if os.path.exists(gl_info["quts"]): + quts_path = gl_info["quts"] +-elif os.path.exists(gl_info["quts2"]): ++elif "quts2" in gl_info and os.path.exists(gl_info["quts2"]): + quts_path = gl_info["quts2"] + if quts_path and os.path.exists(os.path.join(quts_path,'Common','ttypes.py'))\ + and os.path.exists(os.path.join(quts_path,'ImageManagementService','ImageManagementService.py'))\ +-- +2.53.0 + diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0004-dtwrapper-support-upstream-pyfdt-s-zero-arg-to_dtb.patch b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0004-dtwrapper-support-upstream-pyfdt-s-zero-arg-to_dtb.patch new file mode 100644 index 000000000..2912cb27e --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0004-dtwrapper-support-upstream-pyfdt-s-zero-arg-to_dtb.patch @@ -0,0 +1,59 @@ +From 835fdad09596feb26edd903b41d43f59301e0b7c Mon Sep 17 00:00:00 2001 +From: Igor Opaniuk +Date: Sun, 14 Jun 2026 23:28:56 +0200 +Subject: [PATCH] dtwrapper: support upstream pyfdt's zero-arg to_dtb() + +DTWrapper._update_dtb calls self._fdt.to_dtb(self.dtb_mappings), +passing an out-dict that the FDT serializer is expected to populate +with byte-offset mappings for each node. This signature exists in +QDTE's expected pyfdt fork but not in the upstream PyPI release +(pyfdt 0.3 by Neil Armstrong, the only published version): +upstream defines `def to_dtb(self):` with no parameters. Calling +the upstream binding raises: + + TypeError: Fdt.to_dtb() takes 1 positional argument but 2 were given + +The dtb_mappings dict is only read by hexview.HexWindow to highlight +node offsets in the GUI hex viewer; it is never consumed in --nogui +mode. Probe both signatures so QDTE works whether the binding it +finds is the upstream zero-arg form or the internal mapping-capable +form. When the upstream binding is in use, dtb_mappings stays empty +and the hex viewer simply renders without offset highlights. + +Tested on PyPI pyfdt 0.3 packaged as python3-pyfdt-native in +meta-qcom (Yocto wrynose), driving --nogui xbl_config.elf cert +injection on RB3 Gen 2 / QCM6490. + +Upstream-Status: Submitted [https://github.com/qualcomm/DTE/pull/N] +Signed-off-by: Igor Opaniuk +--- + dtwrapper.py | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/dtwrapper.py b/dtwrapper.py +index 9071185..f0faf58 100644 +--- a/dtwrapper.py ++++ b/dtwrapper.py +@@ -1006,7 +1006,18 @@ class DTWrapper: + def _update_dtb(self): + self.dtb_mappings = {} + if self._fdt is not None: +- self.dtb = self._fdt.to_dtb(self.dtb_mappings) ++ # Upstream PyPI pyfdt (0.3, the only released version) exposes ++ # `to_dtb(self)` with no parameters. QDTE was developed against ++ # an internal fork whose `to_dtb()` accepts an out-dict that gets ++ # populated with byte-offset mappings for the GUI hex viewer. ++ # Probe both signatures so we work with either: pass the ++ # mappings dict if accepted, otherwise call the zero-arg form ++ # and leave `dtb_mappings` empty (the hex viewer simply will ++ # not highlight node offsets in that case). ++ try: ++ self.dtb = self._fdt.to_dtb(self.dtb_mappings) ++ except TypeError: ++ self.dtb = self._fdt.to_dtb() + else: + self.dtb = None + +-- +2.53.0 + diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0005-dtwrapper-accept-hex-oct-bin-literals-in-FdtProperty.patch b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0005-dtwrapper-accept-hex-oct-bin-literals-in-FdtProperty.patch new file mode 100644 index 000000000..50bad26c2 --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/files/0005-dtwrapper-accept-hex-oct-bin-literals-in-FdtProperty.patch @@ -0,0 +1,56 @@ +From f500d767cc9059746a9481d629c0e2c572661523 Mon Sep 17 00:00:00 2001 +From: Igor Opaniuk +Date: Sun, 14 Jun 2026 23:31:41 +0200 +Subject: [PATCH] dtwrapper: accept hex/oct/bin literals in FdtPropertyWords + --modify + +DTOperationEditPropVal.apply iterates the value list supplied by +--modify and casts each string token to an integer with bare +int(val), which only accepts base-10 literals. Callers that wish +to feed hexadecimal word values -- the natural format when mirroring +fdtdump's <0x... 0x... 0x...> display, or when piping precomputed +big-endian uint32 words from an external converter -- hit: + + ValueError: invalid literal for int() with base 10: '0x000003a6' + +Switch to int(val, 0), which auto-detects the base from a literal +prefix (0x / 0o / 0b, else decimal). Decimal callers continue to +work unchanged; hex and other prefixed literals now succeed. + +The use case that motivated this: injecting a length-prefixed, +big-endian-packed DER certificate into a Qualcomm xbl_config DTB +property via --modify; the natural output of a one-liner like: + + python3 -c 'import struct; ...; print(";".join("0x%08x" % w ...))' + +Tested on PyPI pyfdt 0.3 packaged as python3-pyfdt-native in +meta-qcom (Yocto wrynose), driving --nogui xbl_config.elf cert +injection on RB3 Gen 2 / QCM6490. + +Upstream-Status: Submitted [https://github.com/qualcomm/DTE/pull/N] +Signed-off-by: Igor Opaniuk +--- + dtwrapper.py | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/dtwrapper.py b/dtwrapper.py +index f0faf58..e75fc35 100644 +--- a/dtwrapper.py ++++ b/dtwrapper.py +@@ -592,7 +592,12 @@ class DTOperationEditPropVal(DTOperation): + for val in self.val_new: + #dtlogger.debug(type(val)) + if isinstance(val,type('')): +- vals.append(int(val)) ++ # int(val, 0) auto-detects the base from a literal ++ # prefix ('0x' hex, '0o' octal, '0b' binary, else ++ # decimal). Plain int(val) only accepts decimal and ++ # rejects '0x...' tokens that callers naturally ++ # supply when mirroring fdtdump output. ++ vals.append(int(val, 0)) + else: + vals.append(val) + #dtlogger.debug("new list is %s"%vals) +-- +2.53.0 + diff --git a/dynamic-layers/openembedded-layer/recipes-devtools/qdte/qdte-native_git.bb b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/qdte-native_git.bb new file mode 100644 index 000000000..da99d2484 --- /dev/null +++ b/dynamic-layers/openembedded-layer/recipes-devtools/qdte/qdte-native_git.bb @@ -0,0 +1,65 @@ +SUMMARY = "Qualcomm Device Tree Editor (QDTE)" +DESCRIPTION = "QDTE is a Tkinter-based Device Tree editor with a --nogui mode for \ +scripted modification of DTB payloads" +HOMEPAGE = "https://github.com/qualcomm/DTE" +LICENSE = "BSD-3-Clause" +LIC_FILES_CHKSUM = "file://LICENSE.txt;md5=57272fa9cc740c745feb331231cca6f2" + +SRC_URI = " \ + git://github.com/qualcomm/DTE.git;protocol=https;branch=main \ + file://0001-controller-make-non_hlos_parser-import-optional.patch \ + file://0002-run-stub-tkinter-for-nogui-mode.patch \ + file://0003-controller-guard-quts2-lookup-on-Linux.patch \ + file://0004-dtwrapper-support-upstream-pyfdt-s-zero-arg-to_dtb.patch \ + file://0005-dtwrapper-accept-hex-oct-bin-literals-in-FdtProperty.patch \ +" +SRCREV = "7e3e493250c6a69a83e4f25dcb37d120e77f9dd8" + +S = "${UNPACKDIR}/${BPN}-${PV}" + +inherit native python3native + +# Runtime imports needed for --nogui mode (after patches applied): +# run.py -> six (tkinter stubbed by 0002) +# controller.py -> non_hlos_parser stub (provided by 0001) +# dtwrapper.py -> pyfdt +DEPENDS += " \ + python3-six-native \ + python3-pyfdt-native \ +" + +do_configure[noexec] = "1" +do_compile[noexec] = "1" + +# QDTE has no install target. Stage the source tree under ${datadir} and +# drop a thin wrapper into ${bindir} so consumers can invoke `qdte` from +# PATH. +do_install() { + install -d "${D}${datadir}/qdte" + cp -r "${S}"/* "${D}${datadir}/qdte/" + + install -d "${D}${bindir}" + cat > "${D}${bindir}/qdte" <\` +# (see assemble.py, Autocmd.py, version_2_assemble.py, XBLConfig/*.py). +# Yocto native sysroots ship only python3, so create an ephemeral PATH +# shim with a \`python\` symlink to the same interpreter that's running +# us. The shim is cleaned up on exit and never written into the +# sysroot bindir, so other recipes are unaffected. +SHIM_DIR=\$(mktemp -d "\${TMPDIR:-/tmp}/qdte-pyshim.XXXXXX") +trap 'rm -rf "\$SHIM_DIR"' EXIT +ln -s "\$(command -v python3)" "\$SHIM_DIR/python" +export PATH="\$SHIM_DIR:\$PATH" + +exec python3 "\${QDTE_DIR}/run.py" "\$@" +EOF + chmod 0755 "${D}${bindir}/qdte" +} + +FILES:${PN} += "${datadir}/qdte"