diff mbox series

[isar-cip-core,RFC,3/4] recipes-core: add swupdate

Message ID 20200625132111.16367-4-Quirin.Gylstorff@siemens.com (mailing list archive)
State Accepted
Headers show
Series A/B Rootfs update with software update | expand

Commit Message

Gylstorff Quirin June 25, 2020, 1:21 p.m. UTC
From: Quirin Gylstorff <quirin.gylstorff@siemens.com>

Add swupdate for A/B software updates. Currently the Round Robin
handler in lua supports efibootguard as bootloader. The u-boot
implementation is outstanding.

Signed-off-by: Quirin Gylstorff <quirin.gylstorff@siemens.com>
---
 classes/kconfig-snippets.bbclass              |  90 ++++
 classes/swupdate-config.bbclass               |  76 +++
 classes/swupdate-img.bbclass                  |  75 +++
 .../swupdate/files/debian/changelog.tmpl      |   6 +
 recipes-core/swupdate/files/debian/compat     |   1 +
 .../swupdate/files/debian/control.tmpl        |  15 +
 recipes-core/swupdate/files/debian/copyright  |  36 ++
 recipes-core/swupdate/files/debian/rules.tmpl |  30 ++
 .../swupdate/files/debian/swupdate.examples   |   2 +
 .../swupdate/files/debian/swupdate.install    |   2 +
 .../swupdate/files/debian/swupdate.manpages   |   5 +
 .../swupdate/files/debian/swupdate.tmpfile    |   2 +
 recipes-core/swupdate/files/debian/watch      |  12 +
 recipes-core/swupdate/files/postinst          |   2 +
 recipes-core/swupdate/files/swupdate.cfg      |   6 +
 .../swupdate/files/swupdate.service.example   |  11 +
 .../swupdate/files/swupdate.socket.example    |  11 +
 .../swupdate/files/swupdate.socket.tmpl       |  13 +
 .../swupdate/files/swupdate_defconfig         |  83 ++++
 .../swupdate_defconfig_efibootguard.snippet   |   3 +
 .../files/swupdate_defconfig_lua.snippet      |   2 +
 .../swupdate_defconfig_luahandler.snippet     |   4 +
 .../files/swupdate_defconfig_mtd.snippet      |   1 +
 .../files/swupdate_defconfig_u-boot.snippet   |   3 +
 .../files/swupdate_defconfig_ubi.snippet      |   6 +
 .../swupdate/files/swupdate_handlers.lua      | 449 ++++++++++++++++++
 recipes-core/swupdate/swupdate.bb             |  54 +++
 27 files changed, 1000 insertions(+)
 create mode 100644 classes/kconfig-snippets.bbclass
 create mode 100644 classes/swupdate-config.bbclass
 create mode 100644 classes/swupdate-img.bbclass
 create mode 100644 recipes-core/swupdate/files/debian/changelog.tmpl
 create mode 100644 recipes-core/swupdate/files/debian/compat
 create mode 100644 recipes-core/swupdate/files/debian/control.tmpl
 create mode 100644 recipes-core/swupdate/files/debian/copyright
 create mode 100755 recipes-core/swupdate/files/debian/rules.tmpl
 create mode 100644 recipes-core/swupdate/files/debian/swupdate.examples
 create mode 100644 recipes-core/swupdate/files/debian/swupdate.install
 create mode 100644 recipes-core/swupdate/files/debian/swupdate.manpages
 create mode 100644 recipes-core/swupdate/files/debian/swupdate.tmpfile
 create mode 100644 recipes-core/swupdate/files/debian/watch
 create mode 100644 recipes-core/swupdate/files/postinst
 create mode 100644 recipes-core/swupdate/files/swupdate.cfg
 create mode 100644 recipes-core/swupdate/files/swupdate.service.example
 create mode 100644 recipes-core/swupdate/files/swupdate.socket.example
 create mode 100644 recipes-core/swupdate/files/swupdate.socket.tmpl
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
 create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
 create mode 100644 recipes-core/swupdate/files/swupdate_handlers.lua
 create mode 100644 recipes-core/swupdate/swupdate.bb

Comments

Jan Kiszka June 26, 2020, 1:05 p.m. UTC | #1
On 25.06.20 15:21, Q. Gylstorff wrote:
> From: Quirin Gylstorff <quirin.gylstorff@siemens.com>
> 
> Add swupdate for A/B software updates. Currently the Round Robin
> handler in lua supports efibootguard as bootloader. The u-boot
> implementation is outstanding.
> 
> Signed-off-by: Quirin Gylstorff <quirin.gylstorff@siemens.com>
> ---
>   classes/kconfig-snippets.bbclass              |  90 ++++
>   classes/swupdate-config.bbclass               |  76 +++
>   classes/swupdate-img.bbclass                  |  75 +++
>   .../swupdate/files/debian/changelog.tmpl      |   6 +
>   recipes-core/swupdate/files/debian/compat     |   1 +
>   .../swupdate/files/debian/control.tmpl        |  15 +
>   recipes-core/swupdate/files/debian/copyright  |  36 ++
>   recipes-core/swupdate/files/debian/rules.tmpl |  30 ++
>   .../swupdate/files/debian/swupdate.examples   |   2 +
>   .../swupdate/files/debian/swupdate.install    |   2 +
>   .../swupdate/files/debian/swupdate.manpages   |   5 +
>   .../swupdate/files/debian/swupdate.tmpfile    |   2 +
>   recipes-core/swupdate/files/debian/watch      |  12 +
>   recipes-core/swupdate/files/postinst          |   2 +
>   recipes-core/swupdate/files/swupdate.cfg      |   6 +
>   .../swupdate/files/swupdate.service.example   |  11 +
>   .../swupdate/files/swupdate.socket.example    |  11 +
>   .../swupdate/files/swupdate.socket.tmpl       |  13 +
>   .../swupdate/files/swupdate_defconfig         |  83 ++++
>   .../swupdate_defconfig_efibootguard.snippet   |   3 +
>   .../files/swupdate_defconfig_lua.snippet      |   2 +
>   .../swupdate_defconfig_luahandler.snippet     |   4 +
>   .../files/swupdate_defconfig_mtd.snippet      |   1 +
>   .../files/swupdate_defconfig_u-boot.snippet   |   3 +
>   .../files/swupdate_defconfig_ubi.snippet      |   6 +
>   .../swupdate/files/swupdate_handlers.lua      | 449 ++++++++++++++++++
>   recipes-core/swupdate/swupdate.bb             |  54 +++
>   27 files changed, 1000 insertions(+)
>   create mode 100644 classes/kconfig-snippets.bbclass
>   create mode 100644 classes/swupdate-config.bbclass
>   create mode 100644 classes/swupdate-img.bbclass
>   create mode 100644 recipes-core/swupdate/files/debian/changelog.tmpl
>   create mode 100644 recipes-core/swupdate/files/debian/compat
>   create mode 100644 recipes-core/swupdate/files/debian/control.tmpl
>   create mode 100644 recipes-core/swupdate/files/debian/copyright
>   create mode 100755 recipes-core/swupdate/files/debian/rules.tmpl
>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.examples
>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.install
>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.manpages
>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.tmpfile
>   create mode 100644 recipes-core/swupdate/files/debian/watch
>   create mode 100644 recipes-core/swupdate/files/postinst
>   create mode 100644 recipes-core/swupdate/files/swupdate.cfg
>   create mode 100644 recipes-core/swupdate/files/swupdate.service.example
>   create mode 100644 recipes-core/swupdate/files/swupdate.socket.example
>   create mode 100644 recipes-core/swupdate/files/swupdate.socket.tmpl
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
>   create mode 100644 recipes-core/swupdate/files/swupdate_handlers.lua
>   create mode 100644 recipes-core/swupdate/swupdate.bb
> 
> diff --git a/classes/kconfig-snippets.bbclass b/classes/kconfig-snippets.bbclass
> new file mode 100644
> index 0000000..d754654
> --- /dev/null
> +++ b/classes/kconfig-snippets.bbclass
> @@ -0,0 +1,90 @@
> +#
> +# CIP Core, generic profile
> +#
> +# Copyright (c) Siemens AG, 2020
> +#
> +# Authors:
> +#  Christian Storm <christian.storm@siemens.com>
> +#
> +# SPDX-License-Identifier: MIT
> +
> +KCONFIG_SNIPPETS = ""
> +
> +# The following function defines the kconfig snippet system
> +# with automatich debian dependency injection
> +#
> +# To define a feature set, the user has to define the following
> +# variable to an empty string:
> +#
> +# KFEATURE_featurename = ""
> +#
> +# Then, required additions to the variables can be defined:
> +#
> +# KFEATURE_featurename[KCONFIG_SNIPPETS] = "file://snippet-file-name.snippet"
> +# KFEATURE_featurename[SRC_URI] = "file://required-file.txt"
> +# KFEATURE_featurename[DEPENDS] = "deb-pkg1 deb-pkg2 deb-pkg3"
> +# KFEATURE_featurename[DEBIAN_DEPENDS] = "deb-pkg1"
> +# KFEATURE_featurename[BUILD_DEB_DEPENDS] = "deb-pkg1,deb-pkg2,deb-pkg3"
> +
> +# The 'KCONFIG_SNIPPETS' flag gives a list of URI entries, where only
> +# file:// is supported. These snippets are appended to the DEFCONFIG file.
> +#
> +# Features can depend on other features via the following mechanism:
> +#
> +# KFEATURE_DEPS[feature1] = "feature2"
> +
> +python () {
> +    requested_features = d.getVar("KFEATURES", True) or ""
> +
> +    features = set(requested_features.split())
> +    old_features = set()
> +    feature_deps = d.getVarFlags("KFEATURE_DEPS") or {}
> +    while old_features != features:
> +        diff_features = old_features.symmetric_difference(features)
> +        old_features = features.copy()
> +        for i in diff_features:
> +            features.update(feature_deps.get(i, "").split())
> +
> +    for f in sorted(features):
> +        bb.debug(2, "Feature: " + f)
> +        varname = "KFEATURE_" + f
> +        dummyvar = d.getVar(varname, False)
> +        if dummyvar == None:
> +            bb.error("Feature var " + f + " must be defined with needed flags.")
> +        else:
> +            feature_flags = d.getVarFlags(varname)
> +            for feature_varname in sorted(feature_flags):
> +                if feature_flags.get(feature_varname, "") != "":
> +                    sep = " "
> +
> +                    # Required to add KCONFIG_SNIPPETS to SRC_URI here,
> +                    # because 'SRC_URI += "${KCONFIG_SNIPPETS}"' would
> +                    # conflict with SRC_APT feature.
> +                    if feature_varname == "KCONFIG_SNIPPETS":
> +                        d.appendVar('SRC_URI',
> +                            " " + feature_flags[feature_varname].strip())
> +
> +                    # BUILD_DEP_DEPENDS and DEBIAN_DEPENDS is ',' separated
> +                    # Only add ',' if there is already something there
> +                    if feature_varname in ["BUILD_DEB_DEPENDS",
> +                                           "DEBIAN_DEPENDS"]:
> +                        sep = "," if d.getVar(feature_varname) else ""
> +
> +                    d.appendVar(feature_varname,
> +                        sep + feature_flags[feature_varname].strip())
> +}
> +
> +# DEFCONFIG must be a predefined bitbake variable and the corresponding file
> +# must exist in the WORKDIR.
> +# The resulting generated config is the same file suffixed with ".gen"
> +
> +do_prepare_build_prepend() {
> +        sh -x
> +        GENCONFIG="${WORKDIR}/${DEFCONFIG}".gen
> +        rm -f "$GENCONFIG"
> +        cp "${WORKDIR}/${DEFCONFIG}" "$GENCONFIG"
> +        for CONFIG_SNIPPET in $(echo "${KCONFIG_SNIPPETS}" | sed 's#file://##g')
> +        do
> +                cat ${WORKDIR}/$CONFIG_SNIPPET >> "$GENCONFIG"
> +        done
> +}
> diff --git a/classes/swupdate-config.bbclass b/classes/swupdate-config.bbclass
> new file mode 100644
> index 0000000..7ce51c5
> --- /dev/null
> +++ b/classes/swupdate-config.bbclass
> @@ -0,0 +1,76 @@
> +#
> +# CIP Core, generic profile
> +#
> +# Copyright (c) Siemens AG, 2020
> +#
> +# Authors:
> +#  Christian Storm <christian.storm@siemens.com>
> +#
> +# SPDX-License-Identifier: MIT
> +
> +# This class manages the config snippets together with their dependencies
> +# to build SWUpdate
> +
> +inherit kconfig-snippets
> +
> +BUILD_DEB_DEPENDS = " \
> +    zlib1g-dev, debhelper, libconfig-dev, libarchive-dev, \
> +    python-sphinx:native, dh-systemd, libsystemd-dev"
> +
> +KFEATURE_lua = ""
> +KFEATURE_lua[BUILD_DEB_DEPENDS] = "liblua5.3-dev"
> +KFEATURE_lua[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_lua.snippet"
> +
> +KFEATURE_luahandler = ""
> +KFEATURE_luahandler[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_luahandler.snippet"
> +KFEATURE_luahandler[SRC_URI] = "file://${SWUPDATE_LUASCRIPT}"
> +
> +KFEATURE_DEPS = ""
> +KFEATURE_DEPS[luahandler] = "lua"
> +
> +KFEATURE_efibootguard = ""
> +KFEATURE_efibootguard[BUILD_DEB_DEPENDS] = "efibootguard-dev"
> +KFEATURE_efibootguard[DEBIAN_DEPENDS] = "efibootguard-dev"
> +KFEATURE_efibootguard[DEPENDS] = "efibootguard-dev"
> +KFEATURE_efibootguard[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_efibootguard.snippet"
> +
> +KFEATURE_mtd = ""
> +KFEATURE_mtd[BUILD_DEB_DEPENDS] = "libmtd-dev"
> +KFEATURE_mtd[DEPENDS] = "mtd-utils"
> +KFEATURE_mtd[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_mtd.snippet"
> +
> +KFEATURE_ubi = ""
> +KFEATURE_ubi[BUILD_DEB_DEPENDS] = "libubi-dev"
> +KFEATURE_ubi[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_ubi.snippet"
> +
> +KFEATURE_DEPS[ubi] = "mtd"
> +
> +KFEATURE_u-boot = ""
> +KFEATURE_u-boot[BUILD_DEB_DEPENDS] = "u-boot-${MACHINE}-dev"
> +KFEATURE_u-boot[DEBIAN_DEPENDS] = "u-boot-tools"
> +KFEATURE_u-boot[DEPENDS] = "${U_BOOT}"
> +KFEATURE_u-boot[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_u-boot.snippet"
> +
> +SWUPDATE_LUASCRIPT ?= "swupdate_handlers.lua"
> +
> +def get_bootloader_featureset(d):
> +    bootloader = d.getVar("BOOTLOADER", True) or ""
> +    if bootloader == "efibootguard":
> +        return "efibootguard"
> +    if bootloader == "u-boot":
> +        return "u-boot"
> +    return ""
> +
> +SWUPDATE_KFEATURES ??= ""
> +KFEATURES = "${SWUPDATE_KFEATURES}"
> +KFEATURES += "${@get_bootloader_featureset(d)}"
> +
> +# Astonishingly, as an anonymous python function, BOOTLOADER is always None
> +# one time before it gets set. So the following must be a task.
> +python do_check_bootloader () {
> +    bootloader = d.getVar("BOOTLOADER", True) or "None"
> +    if not bootloader in ["efibootguard", "u-boot"]:
> +        bb.warn("swupdate: BOOTLOADER set to incompatible value: " + bootloader)
> +}
> +addtask check_bootloader before do_fetch
> +
> diff --git a/classes/swupdate-img.bbclass b/classes/swupdate-img.bbclass
> new file mode 100644
> index 0000000..a21d6ec
> --- /dev/null
> +++ b/classes/swupdate-img.bbclass
> @@ -0,0 +1,75 @@
> +#
> +# CIP Core, generic profile
> +#
> +# Copyright (c) Siemens AG, 2020
> +#
> +# Authors:
> +#  Christian Storm <christian.storm@siemens.com>
> +#  Quirin Gylstorff <quirin.gylstorff@siemens.com>
> +#
> +# SPDX-License-Identifier: MIT
> +
> +SWU_IMAGE_FILE ?= "${PN}-${DISTRO}-${MACHINE}.swu"
> +SWU_DESCRIPTION_FILE ?= "sw-description"
> +SWU_ADDITIONAL_FILES ?= ""
> +SWU_SIGNED ?= ""
> +SWU_SIGNATURE_EXT ?= "sig"
> +SWU_SIGNATURE_TYPE ?= "rsa"
> +
> +IMAGER_INSTALL += "${@'openssl' if bb.utils.to_boolean(d.getVar('SWU_SIGNED')) else ''}"
> +
> +do_swupdate_image[stamp-extra-info] = "${DISTRO}-${MACHINE}"
> +do_swupdate_image[cleandirs] += "${WORKDIR}/swu"
> +do_swupdate_image() {
> +    rm -f '${DEPLOY_DIR_IMAGE}/${SWU_IMAGE_FILE}'
> +    cp '${WORKDIR}/${SWU_DESCRIPTION_FILE}' '${WORKDIR}/swu/${SWU_DESCRIPTION_FILE}'
> +
> +    # Create symlinks for files used in the update image
> +    for file in ${SWU_ADDITIONAL_FILES}; do
> +        if [ -e "${WORKDIR}/$file" ]; then
> +            ln -s "${WORKDIR}/$file" "${WORKDIR}/swu/$file"
> +        else
> +            ln -s "${DEPLOY_DIR_IMAGE}/$file" "${WORKDIR}/swu/$file"
> +        fi
> +    done
> +
> +    # Prepare for signing
> +    sign='${@'x' if bb.utils.to_boolean(d.getVar('SWU_SIGNED')) else ''}'
> +    if [ -n "$sign" ]; then
> +        image_do_mounts
> +        cp -f '${SIGN_KEY}' '${WORKDIR}/dev.key'
> +        test -e '${SIGN_CRT}' && cp -f '${SIGN_CRT}' '${WORKDIR}/dev.crt'
> +
> +        # Fill in file check sums
> +        for file in ${SWU_ADDITIONAL_FILES}; do
> +            sed -i "s:$file-sha256:$(sha256sum '${WORKDIR}/swu/'$file | cut -f 1 -d ' '):g" \
> +                '${WORKDIR}/swu/${SWU_DESCRIPTION_FILE}'
> +        done
> +    fi
> +
> +    cd "${WORKDIR}/swu"
> +    for file in '${SWU_DESCRIPTION_FILE}' ${SWU_ADDITIONAL_FILES}; do
> +        echo "$file"
> +        if [ -n "$sign" -a \
> +             '${SWU_DESCRIPTION_FILE}' = "$file" ]; then
> +            if [ "${SWU_SIGNATURE_TYPE}" = "rsa" ]; then
> +                sudo chroot ${BUILDCHROOT_DIR} /usr/bin/openssl dgst \
> +                    -sha256 -sign '${PP_WORK}/dev.key' \
> +                    '${PP_WORK}/swu/'"$file" \
> +                        > '${WORKDIR}/swu/'"$file".'${SWU_SIGNATURE_EXT}'
> +            elif [ "${SWU_SIGNATURE_TYPE}" = "cms" ]; then
> +                sudo chroot ${BUILDCHROOT_DIR} /usr/bin/openssl cms \
> +                    -sign -in '${PP_WORK}/swu/'"$file" \
> +                    -out '${WORKDIR}/swu/'"$file".'${SWU_SIGNATURE_EXT}' \
> +                    -signer '${PP_WORK}/dev.crt' \
> +                    -inkey '${PP_WORK}/dev.key' \
> +                    -outform DER -nosmimecap -binary
> +            fi
> +            echo "$file".'${SWU_SIGNATURE_EXT}'
> +        fi
> +    done | cpio -ovL -H crc \
> +        > '${DEPLOY_DIR_IMAGE}/${SWU_IMAGE_FILE}'
> +    cd -
> +}
> +
> +addtask swupdate_image before do_build after do_copy_boot_files do_install_imager_deps do_transform_template
> diff --git a/recipes-core/swupdate/files/debian/changelog.tmpl b/recipes-core/swupdate/files/debian/changelog.tmpl
> new file mode 100644
> index 0000000..81087d3
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/changelog.tmpl
> @@ -0,0 +1,6 @@
> +swupdate (${PV}) unstable; urgency=medium
> +
> +  * SWUpdate
> +
> + --  Christian Storm <christian.storm@siemens.com>  Thu, 31 Jan 2019 15:23:56 +0100
> +
> diff --git a/recipes-core/swupdate/files/debian/compat b/recipes-core/swupdate/files/debian/compat
> new file mode 100644
> index 0000000..b4de394
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/compat
> @@ -0,0 +1 @@
> +11
> diff --git a/recipes-core/swupdate/files/debian/control.tmpl b/recipes-core/swupdate/files/debian/control.tmpl
> new file mode 100644
> index 0000000..2b92850
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/control.tmpl
> @@ -0,0 +1,15 @@
> +Source: swupdate
> +Section: embedded
> +Priority: optional
> +Maintainer: Stefano Babic <sbabic@denx.de>
> +Build-Depends: ${BUILD_DEB_DEPENDS}
> +Standards-Version: 4.2.1
> +Homepage: http://sbabic.github.io/swupdate
> +
> +Package: swupdate
> +Architecture: any
> +Depends: ${DEBIAN_DEPENDS}
> +Description: reliable way to update an embedded system
> + This project is thought to help to update an embedded system from a storage media or from network.
> + However, it should be mainly considered as a framework, where further protocols or installers
> + (in SWUpdate they are called handlers) can be easily added to the application.
> diff --git a/recipes-core/swupdate/files/debian/copyright b/recipes-core/swupdate/files/debian/copyright
> new file mode 100644
> index 0000000..f920942
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/copyright
> @@ -0,0 +1,36 @@
> +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
> +Upstream-Name: swupdate
> +Maintainer: Stefano Babic <sbabic@denx.de>
> +Source: http://github.com/sbabic/swupdate
> +
> +Files: *
> +Copyright: 2014-2017 Stefano Babic <sbabic@denx.de>
> +
> +License: GPL-2 with OpenSSL exception
> + This package is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 2 of the License, or
> + (at your option) any later version.
> + .
> + In addition, as a special exception, the author of this
> + program gives permission to link the code of its
> + release with the OpenSSL project's "OpenSSL" library (or
> + with modified versions of it that use the same license as
> + the "OpenSSL" library), and distribute the linked
> + executables. You must obey the GNU General Public
> + License in all respects for all of the code used other
> + than "OpenSSL".  If you modify this file, you may extend
> + this exception to your version of the file, but you are
> + not obligated to do so.  If you do not wish to do so,
> + delete this exception statement from your version.
> + .
> + This package is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + GNU General Public License for more details.
> + .
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <https://www.gnu.org/licenses/>
> + .
> + On Debian systems, the complete text of the GNU General
> + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
> diff --git a/recipes-core/swupdate/files/debian/rules.tmpl b/recipes-core/swupdate/files/debian/rules.tmpl
> new file mode 100755
> index 0000000..54cca57
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/rules.tmpl
> @@ -0,0 +1,30 @@
> +#!/usr/bin/make -f
> +
> +ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
> +export CROSS_COMPILE=$(DEB_HOST_GNU_TYPE)-
> +export CC=$(DEB_HOST_GNU_TYPE)-gcc
> +export LD=$(DEB_HOST_GNU_TYPE)-gcc
> +endif
> +
> +export DH_VERBOSE = 1
> +
> +export DEB_BUILD_MAINT_OPTIONS = hardening=+bindnow
> +
> +documentation: configure
> +	make man
> +
> +configure:
> +	make ${DEFCONFIG}
> +
> +build: documentation configure
> +	dh $@
> +
> +%:
> +	echo $@
> +	dh $@
> +
> +override_dh_installchangelogs:
> +	true
> +
> +override_dh_installdocs:
> +	true
> diff --git a/recipes-core/swupdate/files/debian/swupdate.examples b/recipes-core/swupdate/files/debian/swupdate.examples
> new file mode 100644
> index 0000000..c257b75
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/swupdate.examples
> @@ -0,0 +1,2 @@
> +examples/configuration
> +examples/description
> diff --git a/recipes-core/swupdate/files/debian/swupdate.install b/recipes-core/swupdate/files/debian/swupdate.install
> new file mode 100644
> index 0000000..8957cc6
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/swupdate.install
> @@ -0,0 +1,2 @@
> +swupdate usr/bin
> +swupdate.cfg /etc
> diff --git a/recipes-core/swupdate/files/debian/swupdate.manpages b/recipes-core/swupdate/files/debian/swupdate.manpages
> new file mode 100644
> index 0000000..c3438e0
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/swupdate.manpages
> @@ -0,0 +1,5 @@
> +doc/build/man/swupdate.1
> +doc/build/man/client.1
> +doc/build/man/sendtohawkbit.1
> +doc/build/man/hawkbitcfg.1
> +doc/build/man/progress.1
> diff --git a/recipes-core/swupdate/files/debian/swupdate.tmpfile b/recipes-core/swupdate/files/debian/swupdate.tmpfile
> new file mode 100644
> index 0000000..4743672
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/swupdate.tmpfile
> @@ -0,0 +1,2 @@
> +X /tmp/datadst
> +X /tmp/scripts
> diff --git a/recipes-core/swupdate/files/debian/watch b/recipes-core/swupdate/files/debian/watch
> new file mode 100644
> index 0000000..bc4c53e
> --- /dev/null
> +++ b/recipes-core/swupdate/files/debian/watch
> @@ -0,0 +1,12 @@
> +# Example watch control file for uscan
> +# Rename this file to "watch" and then you can run the "uscan" command
> +# to check for upstream updates and more.
> +# See uscan(1) for format
> +
> +# Compulsory line, this is a version 4 file
> +version=4
> +
> +# GitHub hosted projects
> +opts="filenamemangle="s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%<project>-$1.tar.gz%" \
> +   https://github.com/<user>/swupdate/tags \
> +   (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
> diff --git a/recipes-core/swupdate/files/postinst b/recipes-core/swupdate/files/postinst
> new file mode 100644
> index 0000000..f15ac10
> --- /dev/null
> +++ b/recipes-core/swupdate/files/postinst
> @@ -0,0 +1,2 @@
> +#!/bin/sh
> +deb-systemd-helper enable swupdate.socket || true
> diff --git a/recipes-core/swupdate/files/swupdate.cfg b/recipes-core/swupdate/files/swupdate.cfg
> new file mode 100644
> index 0000000..e0222f1
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate.cfg
> @@ -0,0 +1,6 @@
> +globals :
> +{
> +    verbose = true;
> +    loglevel = 10;
> +    syslog = false;
> +};
> diff --git a/recipes-core/swupdate/files/swupdate.service.example b/recipes-core/swupdate/files/swupdate.service.example
> new file mode 100644
> index 0000000..d0b821e
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate.service.example
> @@ -0,0 +1,11 @@
> +[Unit]
> +Description=SWUpdate daemon
> +Documentation=https://github.com/sbabic/swupdate
> +
> +[Service]
> +Type=simple
> +ExecStart=/usr/bin/swupdate -f /etc/swupdate.cfg
> +KillMode=mixed
> +
> +[Install]
> +WantedBy=multi-user.target
> diff --git a/recipes-core/swupdate/files/swupdate.socket.example b/recipes-core/swupdate/files/swupdate.socket.example
> new file mode 100644
> index 0000000..2b75671
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate.socket.example
> @@ -0,0 +1,11 @@
> +[Unit]
> +Description=SWUpdate socket listener
> +Documentation=https://github.com/sbabic/swupdate
> +Documentation=https://sbabic.github.io/swupdate
> +
> +[Socket]
> +ListenStream=/tmp/sockinstctrl
> +ListenStream=/tmp/swupdateprog
> +
> +[Install]
> +WantedBy=sockets.target
> diff --git a/recipes-core/swupdate/files/swupdate.socket.tmpl b/recipes-core/swupdate/files/swupdate.socket.tmpl
> new file mode 100644
> index 0000000..8e7fc1d
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate.socket.tmpl
> @@ -0,0 +1,13 @@
> +[Unit]
> +Description=SWUpdate socket listener
> +Documentation=https://github.com/sbabic/swupdate
> +Documentation=https://sbabic.github.io/swupdate
> +
> +[Socket]
> +SocketUser=${SWUPDATE_SOCKET_OWNER}
> +SocketGroup=root
> +ListenStream=/tmp/sockinstctrl
> +ListenStream=/tmp/swupdateprog
> +
> +[Install]
> +WantedBy=sockets.target
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig b/recipes-core/swupdate/files/swupdate_defconfig
> new file mode 100644
> index 0000000..9ae7cb5
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig
> @@ -0,0 +1,83 @@
> +#
> +# Automatically generated file; DO NOT EDIT.
> +# Swupdate Configuration
> +#
> +CONFIG_HAVE_DOT_CONFIG=y
> +
> +#
> +# Swupdate Settings
> +#
> +
> +#
> +# General Configuration
> +#
> +# CONFIG_CURL is not set
> +# CONFIG_CURL_SSL is not set
> +CONFIG_SYSTEMD=y
> +CONFIG_SCRIPTS=y
> +# CONFIG_HW_COMPATIBILITY is not set
> +CONFIG_SW_VERSIONS_FILE="/etc/sw-versions"
> +
> +#
> +# Socket Paths
> +#
> +CONFIG_SOCKET_CTRL_PATH="/tmp/sockinstctrl"
> +CONFIG_SOCKET_PROGRESS_PATH="/tmp/swupdateprog"
> +CONFIG_SOCKET_REMOTE_HANDLER_DIRECTORY="/tmp/"
> +# CONFIG_MTD is not set
> +# CONFIG_LUA is not set
> +# CONFIG_LUAPKG is not set
> +# CONFIG_FEATURE_SYSLOG is not set
> +
> +#
> +# Build Options
> +#
> +CONFIG_CROSS_COMPILE=""
> +CONFIG_SYSROOT=""
> +CONFIG_EXTRA_CFLAGS=""
> +CONFIG_EXTRA_LDFLAGS=""
> +CONFIG_EXTRA_LDLIBS=""
> +
> +#
> +# Debugging Options
> +#
> +# CONFIG_DEBUG is not set
> +# CONFIG_WERROR is not set
> +# CONFIG_NOCLEANUP is not set
> +# CONFIG_BOOTLOADER_EBG is not set
> +# CONFIG_UBOOT is not set
> +# CONFIG_BOOTLOADER_NONE is not set
> +# CONFIG_BOOTLOADER_GRUB is not set
> +# CONFIG_DOWNLOAD is not set
> +# CONFIG_DOWNLOAD_SSL is not set
> +# CONFIG_CHANNEL_CURL is not set
> +# CONFIG_HASH_VERIFY=y
> +# CONFIG_SIGNED_IMAGES is not set
> +# CONFIG_ENCRYPTED_IMAGES is not set
> +# CONFIG_SURICATTA is not set
> +# CONFIG_WEBSERVER is not set
> +CONFIG_GUNZIP=y
> +
> +#
> +# Parser Features
> +#
> +CONFIG_LIBCONFIG=y
> +CONFIG_PARSERROOT=""
> +# CONFIG_JSON is not set
> +# CONFIG_LUAEXTERNAL is not set
> +# CONFIG_SETEXTPARSERNAME is not set
> +# CONFIG_SETSWDESCRIPTION is not set
> +
> +#
> +# Image Handlers
> +#
> +CONFIG_RAW=y
> +# CONFIG_LUASCRIPTHANDLER is not set
> +# CONFIG_SHELLSCRIPTHANDLER is not set
> +# CONFIG_HANDLER_IN_LUA is not set
> +# CONFIG_EMBEDDED_LUA_HANDLER is not set
> +# CONFIG_EMBEDDED_LUA_HANDLER_SOURCE is not set
> +CONFIG_ARCHIVE=y
> +# CONFIG_REMOTE_HANDLER is not set
> +# CONFIG_SWUFORWARDER_HANDLER is not set
> +# CONFIG_BOOTLOADERHANDLER is not set
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet b/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
> new file mode 100644
> index 0000000..8e3688c
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
> @@ -0,0 +1,3 @@
> +CONFIG_BOOTLOADER_NONE=n
> +CONFIG_BOOTLOADER_EBG=y
> +CONFIG_BOOTLOADERHANDLER=y
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet b/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
> new file mode 100644
> index 0000000..b39f9df
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
> @@ -0,0 +1,2 @@
> +CONFIG_LUA=y
> +CONFIG_LUAPKG="lua53"
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet b/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
> new file mode 100644
> index 0000000..b4a2de8
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
> @@ -0,0 +1,4 @@
> +CONFIG_LUASCRIPTHANDLER=y
> +CONFIG_HANDLER_IN_LUA=y
> +CONFIG_EMBEDDED_LUA_HANDLER=y
> +CONFIG_EMBEDDED_LUA_HANDLER_SOURCE="swupdate_handlers.lua"
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet b/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
> new file mode 100644
> index 0000000..eab98dd
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
> @@ -0,0 +1 @@
> +CONFIG_MTD=y
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet b/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
> new file mode 100644
> index 0000000..6b5832a
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
> @@ -0,0 +1,3 @@
> +CONFIG_UBOOT=y
> +CONFIG_UBOOT_FWENV="/etc/fw_env.config"
> +CONFIG_BOOTLOADERHANDLER=y
> diff --git a/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet b/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
> new file mode 100644
> index 0000000..d1c7732
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
> @@ -0,0 +1,6 @@
> +CONFIG_UBIVOL=y
> +CONFIG_UBIATTACH=y
> +CONFIG_UBIBLACKLIST=""
> +CONFIG_UBIWHITELIST=""
> +CONFIG_UBIVIDOFFSET=0
> +CONFIG_CFI=y
> diff --git a/recipes-core/swupdate/files/swupdate_handlers.lua b/recipes-core/swupdate/files/swupdate_handlers.lua
> new file mode 100644
> index 0000000..c9b9962
> --- /dev/null
> +++ b/recipes-core/swupdate/files/swupdate_handlers.lua
> @@ -0,0 +1,449 @@
> +--[[
> +
> +    Round-robin Image and File Handler.
> +
> +    Copyright (C) 2019, Siemens AG
> +
> +    Author: Christian Storm <christian.storm@siemens.com>
> +
> +    SPDX-License-Identifier: GPL-2.0-or-later
> +
> +    An `sw-description` file using these handlers may look like:
> +        software =
> +        {
> +            version = "0.1.0";
> +            images: ({
> +                filename = "rootfs.ext4";
> +                device = "sda4,sda5";
> +                type = "roundrobin";
> +                compressed = false;
> +            });
> +            files: ({
> +                filename = "vmlinuz";
> +                path = "vmlinuz";
> +                type = "kernelfile";
> +                device = "sda2,sda3";
> +                filesystem = "vfat";
> +            },
> +            {
> +                filename = "initrd.img";
> +                path = "initrd.img";
> +                type = "kernelfile";
> +                device = "sda2,sda3";
> +                filesystem = "vfat";
> +            });
> +        }
> +
> +    The semantics is as follows: Instead of having a fixed target device,
> +    the 'roundrobin' image handler calculates the target device by parsing
> +    /proc/cmdline, matching the root=<device> kernel parameter against its
> +    'device' attribute's list of devices, and sets the actual target
> +    device to the next 'device' attribute list entry in a round-robin
> +    manner. The actual flashing is done via chain-calling another handler,
> +    defaulting to the "raw" handler.
> +
> +    The 'kernelfile' file handler reuses the 'roundrobin' handler's target
> +    device calculation by reading the actual target device from the same
> +    index into its 'device' attribute's list of devices. The actual placing
> +    of files into this partition is done via chain-calling another handler,
> +    defaulting to the "rawfile" handler.
> +
> +    In the above example, if /dev/sda4 is currently booted according to
> +    /proc/cmdline, /dev/sda5 will be flashed and the vmlinuz and initrd.img
> +    files will be placed on /dev/sda3. If /dev/sda5 is booted, /dev/sda4
> +    will be flashed and the vmlinuz and initrd.img files are placed on
> +    /dev/sda2.
> +    In addition to "classical" device nodes as in this example, partition
> +    UUIDs as reported, e.g., by `blkid -s PARTUUID` are also supported.
> +    UBI volumes are supported as well by specifying a CSV list of
> +    ubi<number>:<label> items.
> +
> +    Configuration is done via an INI-style configuration file located at
> +    /etc/swupdate.handler.ini or via compiled-in configuration (by
> +    embedding the Lua handler script into the SWUpdate binary via using
> +    CONFIG_EMBEDDED_LUA_HANDLER), the latter having precedence over the
> +    former. See the example configuration below.
> +    If uncommenting this example block, it will take precedence over any
> +    /etc/swupdate.handler.ini configuration file.
> +
> +    The chain-called handlers can either be specified in the configuration,
> +    i.e., a static run-time setting, or via the 'chainhandler' property of
> +    an 'image' or 'file' section in the sw-description, with the latter
> +    taking precedence over the former, e.g.,
> +        ...
> +        images: ({
> +                filename = "rootfs.ext4";
> +                device = "sda4,sda5";
> +                type = "roundrobin";
> +                properties: {
> +                    chainhandler = "myraw";
> +                };
> +            });
> +        ...
> +    Such a sw-description fragment will chain-call the imaginary "myraw"
> +    handler regardless of what's been configured in the compiled-in or the
> +    configuration file.
> +    When chain-calling the "rdiff_image" handler, its 'rdiffbase' property
> +    is subject to round-robin as well, i.e., the 'rdiffbase' property is
> +    expected to be a CSV list as for the 'device' property, and the actual
> +    'rdiffbase' property value is calculated following the same round-robin
> +    calculation mechanism stated above prior to chain-calling the actual
> +    "rdiff_image" handler, e.g.,
> +        images: ({
> +                filename = "rootfs.ext4";
> +                type = "roundrobin";
> +                device = "sda4,sda5";
> +                properties: {
> +                    chainhandler = "rdiff_image";
> +                    rdiffbase="sda1,sda2";
> +                };
> +            });
> +    will set the 'rdiffbase' property to /dev/sda2 (/dev/sda1) if /dev/sda4
> +    (/dev/sda5) is the currently booted root file system according to
> +    /proc/cmdline parsing.
> +
> +]]
> +
> +
> +local configuration = [[
> +[bootloader]
> +# Required: bootloader name, uboot and ebg currently supported.
> +name=ebg
> +# Required: bootloader-specific key-value pairs, e.g., for ebg:
> +kernelname=linux.signed.efi
> +# For relying on FAT labels, prefix bootlabels with 'L:', e.g., L:BOOT0.
> +# For using custom labels, i.e., relying on the contents of an EFILABEL
> +# file within the partition, prefix it with 'C:', e.g., C:BOOT0.
> +bootlabel={ "C:BOOT0:", "C:BOOT1:" }
> +
> +# Optional: handler to chain-call for the 'roundrobin' handler,
> +# defaulting to 'raw'
> +[roundrobin]
> +chainhandler=raw
> +
> +# Optional: handler to chain-call for the 'kernelfile' handler,
> +# defaulting to 'rawfile'
> +[kernelfile]
> +chainhandler=rawfile
> +]]
> +
> +-- Default configuration file, tried if no compiled-in config is available.
> +local cfgfile = "/etc/swupdate.handler.ini"
> +
> +-- Table holding the configuration.
> +local config = {}
> +
> +-- Mandatory configuration [section] and keys
> +local BOOTLOADERCFG = {
> +    ebg   = {
> +        bootloader = {"name", "bootlabel", "kernelname"}
> +    },
> +    -- TODO fill with mandatory U-Boot configuration
> +    uboot = {
> +        bootloader = {"name"}
> +    }
> +}
> +
> +-- enum-alikes to make code more readable
> +local BOOTLOADER = { EBG = "ebg", UBOOT = "uboot" }
> +local PARTTYPE   = { UUID = 1, PLAIN = 2, UBI = 3 }
> +
> +-- Target table describing the target device the image is to be/has been flashed to.
> +local rrtarget = {
> +    size = function(self)
> +        local _size = 0
> +        for index in pairs(self) do _size = _size + 1 end
> +        return _size - 1
> +    end
> +}
> +
> +-- Helper function parsing CSV fields of a struct img_type such as
> +-- the "device" fields or the "rdiffbase" property.
> +local get_device_list = function(device_node_csv_list)
> +    local device_list = {}
> +    for item in device_node_csv_list:gmatch("([^,]+)") do
> +        local device_node = item:gsub("/dev/", "")
> +        device_list[#device_list+1] = device_node
> +        device_list[device_node] = #device_list
> +    end
> +    return device_list
> +end
> +
> +-- Helper function to determine device node location.
> +local get_device_path = function(device_node)
> +    if device_node:match("ubi%d+:%S+") then
> +        return 0, device_node, PARTTYPE.UBI
> +    end
> +    local device_path = string.format("/dev/disk/by-partuuid/%s", device_node)
> +    local file = io.open(device_path, "rb" )
> +    if file then
> +        file:close()
> +        return 0, device_path, PARTTYPE.UUID
> +    end
> +    device_path = string.format("/dev/%s", device_node)
> +    file = io.open(device_path, "rb" )
> +    if file then
> +        file:close()
> +        return 0, device_path, PARTTYPE.PLAIN
> +    end
> +    swupdate.error(string.format("Cannot access target device node /dev/{,disk/by-partuuid}/%s", device_node))
> +    return 1, nil, nil
> +end
> +
> +-- Helper function parsing the INI-style configuration.
> +local get_config = function()
> +    -- Return configuration right away if it's already parsed.
> +    if config ~= nil and #config > 0 then
> +        return config
> +    end
> +
> +    -- Get configuration INI-style string.
> +    if not configuration then
> +        swupdate.trace(string.format("No compiled-in config found, trying %s", cfgfile))
> +        local file = io.open(cfgfile, "r" )
> +        if not file then
> +            swupdate.error(string.format("Cannot open config file %s", cfgfile))
> +            return nil
> +        end
> +        configuration = file:read("*a")
> +        file:close()
> +    end
> +    if configuration:sub(-1) ~= "\n" then
> +        configuration=configuration.."\n"
> +    end
> +
> +    -- Parse INI-style contents into config table.
> +    local sec, key, value
> +    for line in configuration:gmatch("(.-)\n") do
> +        if line:match("^%[([%w%p]+)%][%s]*") then
> +            sec = line:match("^%[([%w%p]+)%][%s]*")
> +            config[sec] = {}
> +        elseif sec then
> +            key, value = line:match("^([%w%p]-)=(.*)$")
> +            if key and value then
> +                if tonumber(value)  then value = tonumber(value) end
> +                if value == "true"  then value = true            end
> +                if value == "false" then value = false           end
> +                if value:sub(1,1) == "{" then
> +                    local _value = {}
> +                    for _key, _ in value:gmatch("\"(%S+)\"") do
> +                        table.insert(_value, _key)
> +                    end
> +                    value = _value
> +                end
> +                config[sec][key] = value
> +            else
> +                if not line:match("^$") and not line:match("^#") then
> +                    swupdate.warn(string.format("Syntax error, skipping '%s'", line))
> +                end
> +            end
> +        else
> +            swupdate.error(string.format("Syntax error. no [section] encountered."))
> +            return nil
> +        end
> +    end
> +
> +    -- Check config table for mandatory key existence.
> +    if config["bootloader"] == nil or config["bootloader"]["name"] == nil then
> +        swupdate.error(string.format("Syntax error. no [bootloader] encountered or name= missing therein."))
> +        return nil
> +    end
> +    local bcfg = BOOTLOADERCFG[config.bootloader.name]
> +    if not bcfg then
> +        swupdate.error(string.format("Bootloader unsupported, name=uboot|ebg missing in [bootloader]?."))
> +        return nil
> +    end
> +    for sec, _ in pairs(bcfg) do
> +        for _, key in pairs(bcfg[sec]) do
> +            if config[sec] == nil or config[sec][key] == nil then
> +                swupdate.error(string.format("Mandatory config key %s= in [%s] not found.", key, sec))
> +            end
> +        end
> +    end
> +
> +    return config
> +end
> +
> +-- Round-robin image handler for updating the root partition.
> +function handler_roundrobin(image)
> +    -- Read configuration.
> +    if not get_config() then
> +        swupdate.error("Cannot read configuration.")
> +        return 1
> +    end
> +
> +    -- Check if we can chain-call the handler.
> +    local chained_handler = "raw"
> +    if image.properties ~= nil and image.properties["chainhandler"] ~= nil then
> +        chained_handler = image.properties["chainhandler"]
> +    elseif config["roundrobin"] ~= nil and config["roundrobin"]["chainhandler"] ~= nil then
> +        chained_handler = config["roundrobin"]["chainhandler"]
> +    end
> +    if not swupdate.handler[chained_handler] then
> +        swupdate.error(string.format("'%s' handler not available in SWUpdate distribution.", chained_handler))
> +        return 1
> +    end
> +
> +    -- Get device list for round-robin.
> +    local devices = get_device_list(image.device)
> +    if #devices < 2 then
> +        swupdate.error("Specify at least 2 devices in the device= property for 'roundrobin'.")
> +        return 1
> +    end
> +
> +    -- Check that rrtarget is unset, else a reboot may be pending.
> +    if rrtarget:size() > 0 then
> +        swupdate.warn("The 'roundrobin' handler has been run. Is a reboot pending?")
> +    end
> +
> +    -- Determine current root device.
> +    local file = io.open("/proc/cmdline", "r")
> +    if not file then
> +        swupdate.error("Cannot open /proc/cmdline.")
> +        return 1
> +    end
> +    local cmdline = file:read("*l")
> +    file:close()
> +
> +    local rootparam, rootdevice
> +    for item in cmdline:gmatch("%S+") do
> +        rootparam, rootdevice = item:match("(root=[%u=]*[/dev/]*(%S+))")
> +        if rootparam and rootdevice then break end
> +    end
> +    if not rootdevice then
> +      swupdate.error("Cannot determine current root device.")
> +      return 1
> +    end
> +    swupdate.info(string.format("Current root device is: %s", rootdevice))
> +
> +    if not devices[rootdevice] then
> +        swupdate.error(string.format("Current root device '%s' is not in round-robin root devices list: %s", rootdevice, image.device:gsub("/dev/", "")))
> +        return 1
> +    end
> +
> +    -- Perform round-robin calculation for target.
> +    local err
> +    rrtarget.index = devices[rootdevice] % #devices + 1
> +    rrtarget.device_node = devices[rrtarget.index]
> +    err, rrtarget.device_path, rrtarget.parttype = get_device_path(devices[rrtarget.index])
> +    if err ~= 0 then
> +        return 1
> +    end
> +    swupdate.info(string.format("Using '%s' as 'roundrobin' target via '%s' handler.", rrtarget.device_path, chained_handler))
> +
> +    -- If the chain-called handler is rdiff_image, adapt the rdiffbase property
> +    if chained_handler == "rdiff_image" then
> +        if image.properties ~= nil and image.properties["rdiffbase"] ~= nil then
> +            local rdiffbase_devices = get_device_list(image.properties["rdiffbase"])
> +            if #rdiffbase_devices < 2 then
> +                swupdate.error("Specify at least 2 devices in the rdiffbase= property for 'roundrobin'.")
> +                return 1
> +            end
> +            err, image.propierties["rdiffbase"], _ = get_device_path(rdiffbase_devices[rrtarget.index])
> +            if err ~= 0 then
> +                return 1
> +            end
> +            swupdate.info(string.format("Using device %s as rdiffbase.", image.properties["rdiffbase"]))
> +        else
> +            swupdate.error("Property 'rdiffbase' is missing in sw-description.")
> +            return 1
> +        end
> +    end
> +
> +    -- Actually flash the partition.
> +    local msg
> +    image.type = chained_handler
> +    image.device = rrtarget.device_path
> +    err, msg = swupdate.call_handler(chained_handler, image)
> +    if err ~= 0 then
> +        swupdate.error(string.format("Error chain-calling '%s' handler: %s", chained_handler, (msg or "")))
> +        return 1
> +    end
> +
> +    if config.bootloader.name == BOOTLOADER.EBG then
> +      if rootparam then
> +        local value = cmdline:gsub(
> +            rootparam:gsub("%-", "%%-"),
> +            string.format("root=%s%s",
> +                (rrtarget.parttype == PARTTYPE.PLAIN and "") or (rrtarget.parttype == PARTTYPE.UBI and "") or "PARTUUID=",
> +                 rrtarget.parttype == PARTTYPE.PLAIN and rrtarget.device_path or devices[rrtarget.index]
> +            )
> +        )
> +        swupdate.info(string.format("Setting EFI Bootguard environment: kernelparams=%s", value))
> +        swupdate.set_bootenv("kernelparams", value)
> +      end
> +    elseif config.bootloader.name == BOOTLOADER.UBOOT then
> +        -- Update U-Boot environment.
> +        swupdate.info(string.format("Setting U-Boot environment"))
> +        local value = rrtarget.index
> +        swupdate.set_bootenv("swupdpart", value);
> +    end
> +
> +    return 0
> +end
> +
> +-- File handler for updating kernel files.
> +function handler_kernelfile(image)
> +    -- Check if we can chain-call the handler.
> +    local chained_handler = "rawfile"
> +    if image.properties ~= nil and image.properties["chainhandler"] ~= nil then
> +        chained_handler = image.properties["chainhandler"]
> +    elseif config["kernelfile"] ~= nil and config["kernelfile"]["chainhandler"] ~= nil then
> +        chained_handler = config["kernelfile"]["chainhandler"]
> +    end
> +    if not swupdate.handler[chained_handler] then
> +        swupdate.error(string.format("'%s' handler not available in SWUpdate distribution."), chained_handler)
> +        return 1
> +    end
> +
> +    -- Check that rrtarget is set, else the 'roundrobin' handler hasn't been run.
> +    if rrtarget:size() == 0 then
> +        swupdate.error("The 'roundrobin' handler hasn't been run.")
> +        swupdate.info("Place 'roundrobin' above 'kernelfile' in sw-description.")
> +        return 1
> +    end
> +
> +    -- Get device list for round-robin.
> +    local devices = get_device_list(image.device)
> +    if #devices < 2 then
> +        swupdate.error("Specify at least 2 devices in the device= property for 'kernelfile'.")
> +        return 1
> +    end
> +    if rrtarget.index > #devices then
> +        swupdate.error("Cannot map kernel partition to root partition.")
> +        return 1
> +    end
> +
> +    -- Perform round-robin indexing for target.
> +    local err
> +    err, image.device, _ = get_device_path(devices[rrtarget.index])
> +    if err ~= 0 then
> +        return 1
> +    end
> +    swupdate.info(string.format("Using '%s' as 'kernelfile' target via '%s' handler.", image.device, chained_handler))
> +
> +    -- Actually copy the 'kernelfile' files.
> +    local msg
> +    image.type = chained_handler
> +    err, msg = swupdate.call_handler(chained_handler, image)
> +    if err ~= 0 then
> +        swupdate.error(string.format("Error chain-calling '%s' handler: %s", chained_handler, (msg or "")))
> +        return 1
> +    end
> +
> +    if config.bootloader.name == BOOTLOADER.EBG then
> +        -- Update EFI Boot Guard environment: kernelfile
> +        local value = string.format("%s%s", config.bootloader.bootlabel[rrtarget.index], config.bootloader.kernelname)
> +        swupdate.info(string.format("Setting EFI Bootguard environment: kernelfile=%s", value))
> +        swupdate.set_bootenv("kernelfile", value)
> +    elseif config.bootloader.name == BOOTLOADER.UBOOT then
> +        -- Update U-Boot environment.
> +        swupdate.info(string.format("Setting U-Boot environment"))
> +        -- TODO
> +    end
> +
> +    return 0
> +end
> +
> +swupdate.register_handler("roundrobin", handler_roundrobin, swupdate.HANDLER_MASK.IMAGE_HANDLER)
> +swupdate.register_handler("kernelfile", handler_kernelfile, swupdate.HANDLER_MASK.FILE_HANDLER)
> diff --git a/recipes-core/swupdate/swupdate.bb b/recipes-core/swupdate/swupdate.bb
> new file mode 100644
> index 0000000..9c58f7d
> --- /dev/null
> +++ b/recipes-core/swupdate/swupdate.bb
> @@ -0,0 +1,54 @@
> +#
> +# CIP Core, generic profile
> +#
> +# Copyright (c) Siemens AG, 2020
> +#
> +# Authors:
> +#  Quirin Gylstorff <quirin.gylstorff@siemens.com>
> +#
> +# SPDX-License-Identifier: MIT
> +
> +hDESCRIPTION = "swupdate utility for software updates"
> +HOMEPAGE= "https://github.com/sbabic/swupdate"
> +LICENSE = "GPL-2.0"
> +LIC_FILES_CHKSUM = "file://${LAYERDIR_isar}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe"
> +
> +SRC_URI = "gitsm://code.siemens.com/mirror/swupdate.git;branch=master;protocol=https"

Internal mirror. You need to go back to upstream.

And do we actually need gitsm? It is not a mature feature of bitbake, 
thus generally discouraged.

Jan
Gylstorff Quirin June 29, 2020, 9:03 a.m. UTC | #2
On 6/26/20 3:05 PM, Jan Kiszka wrote:
> On 25.06.20 15:21, Q. Gylstorff wrote:
>> From: Quirin Gylstorff <quirin.gylstorff@siemens.com>
>>
>> Add swupdate for A/B software updates. Currently the Round Robin
>> handler in lua supports efibootguard as bootloader. The u-boot
>> implementation is outstanding.
>>
>> Signed-off-by: Quirin Gylstorff <quirin.gylstorff@siemens.com>
>> ---
>>   classes/kconfig-snippets.bbclass              |  90 ++++
>>   classes/swupdate-config.bbclass               |  76 +++
>>   classes/swupdate-img.bbclass                  |  75 +++
>>   .../swupdate/files/debian/changelog.tmpl      |   6 +
>>   recipes-core/swupdate/files/debian/compat     |   1 +
>>   .../swupdate/files/debian/control.tmpl        |  15 +
>>   recipes-core/swupdate/files/debian/copyright  |  36 ++
>>   recipes-core/swupdate/files/debian/rules.tmpl |  30 ++
>>   .../swupdate/files/debian/swupdate.examples   |   2 +
>>   .../swupdate/files/debian/swupdate.install    |   2 +
>>   .../swupdate/files/debian/swupdate.manpages   |   5 +
>>   .../swupdate/files/debian/swupdate.tmpfile    |   2 +
>>   recipes-core/swupdate/files/debian/watch      |  12 +
>>   recipes-core/swupdate/files/postinst          |   2 +
>>   recipes-core/swupdate/files/swupdate.cfg      |   6 +
>>   .../swupdate/files/swupdate.service.example   |  11 +
>>   .../swupdate/files/swupdate.socket.example    |  11 +
>>   .../swupdate/files/swupdate.socket.tmpl       |  13 +
>>   .../swupdate/files/swupdate_defconfig         |  83 ++++
>>   .../swupdate_defconfig_efibootguard.snippet   |   3 +
>>   .../files/swupdate_defconfig_lua.snippet      |   2 +
>>   .../swupdate_defconfig_luahandler.snippet     |   4 +
>>   .../files/swupdate_defconfig_mtd.snippet      |   1 +
>>   .../files/swupdate_defconfig_u-boot.snippet   |   3 +
>>   .../files/swupdate_defconfig_ubi.snippet      |   6 +
>>   .../swupdate/files/swupdate_handlers.lua      | 449 ++++++++++++++++++
>>   recipes-core/swupdate/swupdate.bb             |  54 +++
>>   27 files changed, 1000 insertions(+)
>>   create mode 100644 classes/kconfig-snippets.bbclass
>>   create mode 100644 classes/swupdate-config.bbclass
>>   create mode 100644 classes/swupdate-img.bbclass
>>   create mode 100644 recipes-core/swupdate/files/debian/changelog.tmpl
>>   create mode 100644 recipes-core/swupdate/files/debian/compat
>>   create mode 100644 recipes-core/swupdate/files/debian/control.tmpl
>>   create mode 100644 recipes-core/swupdate/files/debian/copyright
>>   create mode 100755 recipes-core/swupdate/files/debian/rules.tmpl
>>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.examples
>>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.install
>>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.manpages
>>   create mode 100644 recipes-core/swupdate/files/debian/swupdate.tmpfile
>>   create mode 100644 recipes-core/swupdate/files/debian/watch
>>   create mode 100644 recipes-core/swupdate/files/postinst
>>   create mode 100644 recipes-core/swupdate/files/swupdate.cfg
>>   create mode 100644 recipes-core/swupdate/files/swupdate.service.example
>>   create mode 100644 recipes-core/swupdate/files/swupdate.socket.example
>>   create mode 100644 recipes-core/swupdate/files/swupdate.socket.tmpl
>>   create mode 100644 recipes-core/swupdate/files/swupdate_defconfig
>>   create mode 100644 
>> recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
>>   create mode 100644 
>> recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
>>   create mode 100644 
>> recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
>>   create mode 100644 
>> recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
>>   create mode 100644 
>> recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
>>   create mode 100644 
>> recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
>>   create mode 100644 recipes-core/swupdate/files/swupdate_handlers.lua
>>   create mode 100644 recipes-core/swupdate/swupdate.bb
>>
>> diff --git a/classes/kconfig-snippets.bbclass 
>> b/classes/kconfig-snippets.bbclass
>> new file mode 100644
>> index 0000000..d754654
>> --- /dev/null
>> +++ b/classes/kconfig-snippets.bbclass
>> @@ -0,0 +1,90 @@
>> +#
>> +# CIP Core, generic profile
>> +#
>> +# Copyright (c) Siemens AG, 2020
>> +#
>> +# Authors:
>> +#  Christian Storm <christian.storm@siemens.com>
>> +#
>> +# SPDX-License-Identifier: MIT
>> +
>> +KCONFIG_SNIPPETS = ""
>> +
>> +# The following function defines the kconfig snippet system
>> +# with automatich debian dependency injection
>> +#
>> +# To define a feature set, the user has to define the following
>> +# variable to an empty string:
>> +#
>> +# KFEATURE_featurename = ""
>> +#
>> +# Then, required additions to the variables can be defined:
>> +#
>> +# KFEATURE_featurename[KCONFIG_SNIPPETS] = 
>> "file://snippet-file-name.snippet"
>> +# KFEATURE_featurename[SRC_URI] = "file://required-file.txt"
>> +# KFEATURE_featurename[DEPENDS] = "deb-pkg1 deb-pkg2 deb-pkg3"
>> +# KFEATURE_featurename[DEBIAN_DEPENDS] = "deb-pkg1"
>> +# KFEATURE_featurename[BUILD_DEB_DEPENDS] = "deb-pkg1,deb-pkg2,deb-pkg3"
>> +
>> +# The 'KCONFIG_SNIPPETS' flag gives a list of URI entries, where only
>> +# file:// is supported. These snippets are appended to the DEFCONFIG 
>> file.
>> +#
>> +# Features can depend on other features via the following mechanism:
>> +#
>> +# KFEATURE_DEPS[feature1] = "feature2"
>> +
>> +python () {
>> +    requested_features = d.getVar("KFEATURES", True) or ""
>> +
>> +    features = set(requested_features.split())
>> +    old_features = set()
>> +    feature_deps = d.getVarFlags("KFEATURE_DEPS") or {}
>> +    while old_features != features:
>> +        diff_features = old_features.symmetric_difference(features)
>> +        old_features = features.copy()
>> +        for i in diff_features:
>> +            features.update(feature_deps.get(i, "").split())
>> +
>> +    for f in sorted(features):
>> +        bb.debug(2, "Feature: " + f)
>> +        varname = "KFEATURE_" + f
>> +        dummyvar = d.getVar(varname, False)
>> +        if dummyvar == None:
>> +            bb.error("Feature var " + f + " must be defined with 
>> needed flags.")
>> +        else:
>> +            feature_flags = d.getVarFlags(varname)
>> +            for feature_varname in sorted(feature_flags):
>> +                if feature_flags.get(feature_varname, "") != "":
>> +                    sep = " "
>> +
>> +                    # Required to add KCONFIG_SNIPPETS to SRC_URI here,
>> +                    # because 'SRC_URI += "${KCONFIG_SNIPPETS}"' would
>> +                    # conflict with SRC_APT feature.
>> +                    if feature_varname == "KCONFIG_SNIPPETS":
>> +                        d.appendVar('SRC_URI',
>> +                            " " + 
>> feature_flags[feature_varname].strip())
>> +
>> +                    # BUILD_DEP_DEPENDS and DEBIAN_DEPENDS is ',' 
>> separated
>> +                    # Only add ',' if there is already something there
>> +                    if feature_varname in ["BUILD_DEB_DEPENDS",
>> +                                           "DEBIAN_DEPENDS"]:
>> +                        sep = "," if d.getVar(feature_varname) else ""
>> +
>> +                    d.appendVar(feature_varname,
>> +                        sep + feature_flags[feature_varname].strip())
>> +}
>> +
>> +# DEFCONFIG must be a predefined bitbake variable and the 
>> corresponding file
>> +# must exist in the WORKDIR.
>> +# The resulting generated config is the same file suffixed with ".gen"
>> +
>> +do_prepare_build_prepend() {
>> +        sh -x
>> +        GENCONFIG="${WORKDIR}/${DEFCONFIG}".gen
>> +        rm -f "$GENCONFIG"
>> +        cp "${WORKDIR}/${DEFCONFIG}" "$GENCONFIG"
>> +        for CONFIG_SNIPPET in $(echo "${KCONFIG_SNIPPETS}" | sed 
>> 's#file://##g')
>> +        do
>> +                cat ${WORKDIR}/$CONFIG_SNIPPET >> "$GENCONFIG"
>> +        done
>> +}
>> diff --git a/classes/swupdate-config.bbclass 
>> b/classes/swupdate-config.bbclass
>> new file mode 100644
>> index 0000000..7ce51c5
>> --- /dev/null
>> +++ b/classes/swupdate-config.bbclass
>> @@ -0,0 +1,76 @@
>> +#
>> +# CIP Core, generic profile
>> +#
>> +# Copyright (c) Siemens AG, 2020
>> +#
>> +# Authors:
>> +#  Christian Storm <christian.storm@siemens.com>
>> +#
>> +# SPDX-License-Identifier: MIT
>> +
>> +# This class manages the config snippets together with their 
>> dependencies
>> +# to build SWUpdate
>> +
>> +inherit kconfig-snippets
>> +
>> +BUILD_DEB_DEPENDS = " \
>> +    zlib1g-dev, debhelper, libconfig-dev, libarchive-dev, \
>> +    python-sphinx:native, dh-systemd, libsystemd-dev"
>> +
>> +KFEATURE_lua = ""
>> +KFEATURE_lua[BUILD_DEB_DEPENDS] = "liblua5.3-dev"
>> +KFEATURE_lua[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_lua.snippet"
>> +
>> +KFEATURE_luahandler = ""
>> +KFEATURE_luahandler[KCONFIG_SNIPPETS] = 
>> "file://swupdate_defconfig_luahandler.snippet"
>> +KFEATURE_luahandler[SRC_URI] = "file://${SWUPDATE_LUASCRIPT}"
>> +
>> +KFEATURE_DEPS = ""
>> +KFEATURE_DEPS[luahandler] = "lua"
>> +
>> +KFEATURE_efibootguard = ""
>> +KFEATURE_efibootguard[BUILD_DEB_DEPENDS] = "efibootguard-dev"
>> +KFEATURE_efibootguard[DEBIAN_DEPENDS] = "efibootguard-dev"
>> +KFEATURE_efibootguard[DEPENDS] = "efibootguard-dev"
>> +KFEATURE_efibootguard[KCONFIG_SNIPPETS] = 
>> "file://swupdate_defconfig_efibootguard.snippet"
>> +
>> +KFEATURE_mtd = ""
>> +KFEATURE_mtd[BUILD_DEB_DEPENDS] = "libmtd-dev"
>> +KFEATURE_mtd[DEPENDS] = "mtd-utils"
>> +KFEATURE_mtd[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_mtd.snippet"
>> +
>> +KFEATURE_ubi = ""
>> +KFEATURE_ubi[BUILD_DEB_DEPENDS] = "libubi-dev"
>> +KFEATURE_ubi[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_ubi.snippet"
>> +
>> +KFEATURE_DEPS[ubi] = "mtd"
>> +
>> +KFEATURE_u-boot = ""
>> +KFEATURE_u-boot[BUILD_DEB_DEPENDS] = "u-boot-${MACHINE}-dev"
>> +KFEATURE_u-boot[DEBIAN_DEPENDS] = "u-boot-tools"
>> +KFEATURE_u-boot[DEPENDS] = "${U_BOOT}"
>> +KFEATURE_u-boot[KCONFIG_SNIPPETS] = 
>> "file://swupdate_defconfig_u-boot.snippet"
>> +
>> +SWUPDATE_LUASCRIPT ?= "swupdate_handlers.lua"
>> +
>> +def get_bootloader_featureset(d):
>> +    bootloader = d.getVar("BOOTLOADER", True) or ""
>> +    if bootloader == "efibootguard":
>> +        return "efibootguard"
>> +    if bootloader == "u-boot":
>> +        return "u-boot"
>> +    return ""
>> +
>> +SWUPDATE_KFEATURES ??= ""
>> +KFEATURES = "${SWUPDATE_KFEATURES}"
>> +KFEATURES += "${@get_bootloader_featureset(d)}"
>> +
>> +# Astonishingly, as an anonymous python function, BOOTLOADER is 
>> always None
>> +# one time before it gets set. So the following must be a task.
>> +python do_check_bootloader () {
>> +    bootloader = d.getVar("BOOTLOADER", True) or "None"
>> +    if not bootloader in ["efibootguard", "u-boot"]:
>> +        bb.warn("swupdate: BOOTLOADER set to incompatible value: " + 
>> bootloader)
>> +}
>> +addtask check_bootloader before do_fetch
>> +
>> diff --git a/classes/swupdate-img.bbclass b/classes/swupdate-img.bbclass
>> new file mode 100644
>> index 0000000..a21d6ec
>> --- /dev/null
>> +++ b/classes/swupdate-img.bbclass
>> @@ -0,0 +1,75 @@
>> +#
>> +# CIP Core, generic profile
>> +#
>> +# Copyright (c) Siemens AG, 2020
>> +#
>> +# Authors:
>> +#  Christian Storm <christian.storm@siemens.com>
>> +#  Quirin Gylstorff <quirin.gylstorff@siemens.com>
>> +#
>> +# SPDX-License-Identifier: MIT
>> +
>> +SWU_IMAGE_FILE ?= "${PN}-${DISTRO}-${MACHINE}.swu"
>> +SWU_DESCRIPTION_FILE ?= "sw-description"
>> +SWU_ADDITIONAL_FILES ?= ""
>> +SWU_SIGNED ?= ""
>> +SWU_SIGNATURE_EXT ?= "sig"
>> +SWU_SIGNATURE_TYPE ?= "rsa"
>> +
>> +IMAGER_INSTALL += "${@'openssl' if 
>> bb.utils.to_boolean(d.getVar('SWU_SIGNED')) else ''}"
>> +
>> +do_swupdate_image[stamp-extra-info] = "${DISTRO}-${MACHINE}"
>> +do_swupdate_image[cleandirs] += "${WORKDIR}/swu"
>> +do_swupdate_image() {
>> +    rm -f '${DEPLOY_DIR_IMAGE}/${SWU_IMAGE_FILE}'
>> +    cp '${WORKDIR}/${SWU_DESCRIPTION_FILE}' 
>> '${WORKDIR}/swu/${SWU_DESCRIPTION_FILE}'
>> +
>> +    # Create symlinks for files used in the update image
>> +    for file in ${SWU_ADDITIONAL_FILES}; do
>> +        if [ -e "${WORKDIR}/$file" ]; then
>> +            ln -s "${WORKDIR}/$file" "${WORKDIR}/swu/$file"
>> +        else
>> +            ln -s "${DEPLOY_DIR_IMAGE}/$file" "${WORKDIR}/swu/$file"
>> +        fi
>> +    done
>> +
>> +    # Prepare for signing
>> +    sign='${@'x' if bb.utils.to_boolean(d.getVar('SWU_SIGNED')) else 
>> ''}'
>> +    if [ -n "$sign" ]; then
>> +        image_do_mounts
>> +        cp -f '${SIGN_KEY}' '${WORKDIR}/dev.key'
>> +        test -e '${SIGN_CRT}' && cp -f '${SIGN_CRT}' 
>> '${WORKDIR}/dev.crt'
>> +
>> +        # Fill in file check sums
>> +        for file in ${SWU_ADDITIONAL_FILES}; do
>> +            sed -i "s:$file-sha256:$(sha256sum '${WORKDIR}/swu/'$file 
>> | cut -f 1 -d ' '):g" \
>> +                '${WORKDIR}/swu/${SWU_DESCRIPTION_FILE}'
>> +        done
>> +    fi
>> +
>> +    cd "${WORKDIR}/swu"
>> +    for file in '${SWU_DESCRIPTION_FILE}' ${SWU_ADDITIONAL_FILES}; do
>> +        echo "$file"
>> +        if [ -n "$sign" -a \
>> +             '${SWU_DESCRIPTION_FILE}' = "$file" ]; then
>> +            if [ "${SWU_SIGNATURE_TYPE}" = "rsa" ]; then
>> +                sudo chroot ${BUILDCHROOT_DIR} /usr/bin/openssl dgst \
>> +                    -sha256 -sign '${PP_WORK}/dev.key' \
>> +                    '${PP_WORK}/swu/'"$file" \
>> +                        > 
>> '${WORKDIR}/swu/'"$file".'${SWU_SIGNATURE_EXT}'
>> +            elif [ "${SWU_SIGNATURE_TYPE}" = "cms" ]; then
>> +                sudo chroot ${BUILDCHROOT_DIR} /usr/bin/openssl cms \
>> +                    -sign -in '${PP_WORK}/swu/'"$file" \
>> +                    -out 
>> '${WORKDIR}/swu/'"$file".'${SWU_SIGNATURE_EXT}' \
>> +                    -signer '${PP_WORK}/dev.crt' \
>> +                    -inkey '${PP_WORK}/dev.key' \
>> +                    -outform DER -nosmimecap -binary
>> +            fi
>> +            echo "$file".'${SWU_SIGNATURE_EXT}'
>> +        fi
>> +    done | cpio -ovL -H crc \
>> +        > '${DEPLOY_DIR_IMAGE}/${SWU_IMAGE_FILE}'
>> +    cd -
>> +}
>> +
>> +addtask swupdate_image before do_build after do_copy_boot_files 
>> do_install_imager_deps do_transform_template
>> diff --git a/recipes-core/swupdate/files/debian/changelog.tmpl 
>> b/recipes-core/swupdate/files/debian/changelog.tmpl
>> new file mode 100644
>> index 0000000..81087d3
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/changelog.tmpl
>> @@ -0,0 +1,6 @@
>> +swupdate (${PV}) unstable; urgency=medium
>> +
>> +  * SWUpdate
>> +
>> + --  Christian Storm <christian.storm@siemens.com>  Thu, 31 Jan 2019 
>> 15:23:56 +0100
>> +
>> diff --git a/recipes-core/swupdate/files/debian/compat 
>> b/recipes-core/swupdate/files/debian/compat
>> new file mode 100644
>> index 0000000..b4de394
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/compat
>> @@ -0,0 +1 @@
>> +11
>> diff --git a/recipes-core/swupdate/files/debian/control.tmpl 
>> b/recipes-core/swupdate/files/debian/control.tmpl
>> new file mode 100644
>> index 0000000..2b92850
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/control.tmpl
>> @@ -0,0 +1,15 @@
>> +Source: swupdate
>> +Section: embedded
>> +Priority: optional
>> +Maintainer: Stefano Babic <sbabic@denx.de>
>> +Build-Depends: ${BUILD_DEB_DEPENDS}
>> +Standards-Version: 4.2.1
>> +Homepage: http://sbabic.github.io/swupdate
>> +
>> +Package: swupdate
>> +Architecture: any
>> +Depends: ${DEBIAN_DEPENDS}
>> +Description: reliable way to update an embedded system
>> + This project is thought to help to update an embedded system from a 
>> storage media or from network.
>> + However, it should be mainly considered as a framework, where 
>> further protocols or installers
>> + (in SWUpdate they are called handlers) can be easily added to the 
>> application.
>> diff --git a/recipes-core/swupdate/files/debian/copyright 
>> b/recipes-core/swupdate/files/debian/copyright
>> new file mode 100644
>> index 0000000..f920942
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/copyright
>> @@ -0,0 +1,36 @@
>> +Format: 
>> https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
>> +Upstream-Name: swupdate
>> +Maintainer: Stefano Babic <sbabic@denx.de>
>> +Source: http://github.com/sbabic/swupdate
>> +
>> +Files: *
>> +Copyright: 2014-2017 Stefano Babic <sbabic@denx.de>
>> +
>> +License: GPL-2 with OpenSSL exception
>> + This package is free software; you can redistribute it and/or modify
>> + it under the terms of the GNU General Public License as published by
>> + the Free Software Foundation; either version 2 of the License, or
>> + (at your option) any later version.
>> + .
>> + In addition, as a special exception, the author of this
>> + program gives permission to link the code of its
>> + release with the OpenSSL project's "OpenSSL" library (or
>> + with modified versions of it that use the same license as
>> + the "OpenSSL" library), and distribute the linked
>> + executables. You must obey the GNU General Public
>> + License in all respects for all of the code used other
>> + than "OpenSSL".  If you modify this file, you may extend
>> + this exception to your version of the file, but you are
>> + not obligated to do so.  If you do not wish to do so,
>> + delete this exception statement from your version.
>> + .
>> + This package is distributed in the hope that it will be useful,
>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + GNU General Public License for more details.
>> + .
>> + You should have received a copy of the GNU General Public License
>> + along with this program. If not, see <https://www.gnu.org/licenses/>
>> + .
>> + On Debian systems, the complete text of the GNU General
>> + Public License version 2 can be found in 
>> "/usr/share/common-licenses/GPL-2".
>> diff --git a/recipes-core/swupdate/files/debian/rules.tmpl 
>> b/recipes-core/swupdate/files/debian/rules.tmpl
>> new file mode 100755
>> index 0000000..54cca57
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/rules.tmpl
>> @@ -0,0 +1,30 @@
>> +#!/usr/bin/make -f
>> +
>> +ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
>> +export CROSS_COMPILE=$(DEB_HOST_GNU_TYPE)-
>> +export CC=$(DEB_HOST_GNU_TYPE)-gcc
>> +export LD=$(DEB_HOST_GNU_TYPE)-gcc
>> +endif
>> +
>> +export DH_VERBOSE = 1
>> +
>> +export DEB_BUILD_MAINT_OPTIONS = hardening=+bindnow
>> +
>> +documentation: configure
>> +    make man
>> +
>> +configure:
>> +    make ${DEFCONFIG}
>> +
>> +build: documentation configure
>> +    dh $@
>> +
>> +%:
>> +    echo $@
>> +    dh $@
>> +
>> +override_dh_installchangelogs:
>> +    true
>> +
>> +override_dh_installdocs:
>> +    true
>> diff --git a/recipes-core/swupdate/files/debian/swupdate.examples 
>> b/recipes-core/swupdate/files/debian/swupdate.examples
>> new file mode 100644
>> index 0000000..c257b75
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/swupdate.examples
>> @@ -0,0 +1,2 @@
>> +examples/configuration
>> +examples/description
>> diff --git a/recipes-core/swupdate/files/debian/swupdate.install 
>> b/recipes-core/swupdate/files/debian/swupdate.install
>> new file mode 100644
>> index 0000000..8957cc6
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/swupdate.install
>> @@ -0,0 +1,2 @@
>> +swupdate usr/bin
>> +swupdate.cfg /etc
>> diff --git a/recipes-core/swupdate/files/debian/swupdate.manpages 
>> b/recipes-core/swupdate/files/debian/swupdate.manpages
>> new file mode 100644
>> index 0000000..c3438e0
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/swupdate.manpages
>> @@ -0,0 +1,5 @@
>> +doc/build/man/swupdate.1
>> +doc/build/man/client.1
>> +doc/build/man/sendtohawkbit.1
>> +doc/build/man/hawkbitcfg.1
>> +doc/build/man/progress.1
>> diff --git a/recipes-core/swupdate/files/debian/swupdate.tmpfile 
>> b/recipes-core/swupdate/files/debian/swupdate.tmpfile
>> new file mode 100644
>> index 0000000..4743672
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/swupdate.tmpfile
>> @@ -0,0 +1,2 @@
>> +X /tmp/datadst
>> +X /tmp/scripts
>> diff --git a/recipes-core/swupdate/files/debian/watch 
>> b/recipes-core/swupdate/files/debian/watch
>> new file mode 100644
>> index 0000000..bc4c53e
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/debian/watch
>> @@ -0,0 +1,12 @@
>> +# Example watch control file for uscan
>> +# Rename this file to "watch" and then you can run the "uscan" command
>> +# to check for upstream updates and more.
>> +# See uscan(1) for format
>> +
>> +# Compulsory line, this is a version 4 file
>> +version=4
>> +
>> +# GitHub hosted projects
>> +opts="filenamemangle="s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%<project>-$1.tar.gz%" 
>> \
>> +   https://github.com/<user>/swupdate/tags \
>> +   (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
>> diff --git a/recipes-core/swupdate/files/postinst 
>> b/recipes-core/swupdate/files/postinst
>> new file mode 100644
>> index 0000000..f15ac10
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/postinst
>> @@ -0,0 +1,2 @@
>> +#!/bin/sh
>> +deb-systemd-helper enable swupdate.socket || true
>> diff --git a/recipes-core/swupdate/files/swupdate.cfg 
>> b/recipes-core/swupdate/files/swupdate.cfg
>> new file mode 100644
>> index 0000000..e0222f1
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate.cfg
>> @@ -0,0 +1,6 @@
>> +globals :
>> +{
>> +    verbose = true;
>> +    loglevel = 10;
>> +    syslog = false;
>> +};
>> diff --git a/recipes-core/swupdate/files/swupdate.service.example 
>> b/recipes-core/swupdate/files/swupdate.service.example
>> new file mode 100644
>> index 0000000..d0b821e
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate.service.example
>> @@ -0,0 +1,11 @@
>> +[Unit]
>> +Description=SWUpdate daemon
>> +Documentation=https://github.com/sbabic/swupdate
>> +
>> +[Service]
>> +Type=simple
>> +ExecStart=/usr/bin/swupdate -f /etc/swupdate.cfg
>> +KillMode=mixed
>> +
>> +[Install]
>> +WantedBy=multi-user.target
>> diff --git a/recipes-core/swupdate/files/swupdate.socket.example 
>> b/recipes-core/swupdate/files/swupdate.socket.example
>> new file mode 100644
>> index 0000000..2b75671
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate.socket.example
>> @@ -0,0 +1,11 @@
>> +[Unit]
>> +Description=SWUpdate socket listener
>> +Documentation=https://github.com/sbabic/swupdate
>> +Documentation=https://sbabic.github.io/swupdate
>> +
>> +[Socket]
>> +ListenStream=/tmp/sockinstctrl
>> +ListenStream=/tmp/swupdateprog
>> +
>> +[Install]
>> +WantedBy=sockets.target
>> diff --git a/recipes-core/swupdate/files/swupdate.socket.tmpl 
>> b/recipes-core/swupdate/files/swupdate.socket.tmpl
>> new file mode 100644
>> index 0000000..8e7fc1d
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate.socket.tmpl
>> @@ -0,0 +1,13 @@
>> +[Unit]
>> +Description=SWUpdate socket listener
>> +Documentation=https://github.com/sbabic/swupdate
>> +Documentation=https://sbabic.github.io/swupdate
>> +
>> +[Socket]
>> +SocketUser=${SWUPDATE_SOCKET_OWNER}
>> +SocketGroup=root
>> +ListenStream=/tmp/sockinstctrl
>> +ListenStream=/tmp/swupdateprog
>> +
>> +[Install]
>> +WantedBy=sockets.target
>> diff --git a/recipes-core/swupdate/files/swupdate_defconfig 
>> b/recipes-core/swupdate/files/swupdate_defconfig
>> new file mode 100644
>> index 0000000..9ae7cb5
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig
>> @@ -0,0 +1,83 @@
>> +#
>> +# Automatically generated file; DO NOT EDIT.
>> +# Swupdate Configuration
>> +#
>> +CONFIG_HAVE_DOT_CONFIG=y
>> +
>> +#
>> +# Swupdate Settings
>> +#
>> +
>> +#
>> +# General Configuration
>> +#
>> +# CONFIG_CURL is not set
>> +# CONFIG_CURL_SSL is not set
>> +CONFIG_SYSTEMD=y
>> +CONFIG_SCRIPTS=y
>> +# CONFIG_HW_COMPATIBILITY is not set
>> +CONFIG_SW_VERSIONS_FILE="/etc/sw-versions"
>> +
>> +#
>> +# Socket Paths
>> +#
>> +CONFIG_SOCKET_CTRL_PATH="/tmp/sockinstctrl"
>> +CONFIG_SOCKET_PROGRESS_PATH="/tmp/swupdateprog"
>> +CONFIG_SOCKET_REMOTE_HANDLER_DIRECTORY="/tmp/"
>> +# CONFIG_MTD is not set
>> +# CONFIG_LUA is not set
>> +# CONFIG_LUAPKG is not set
>> +# CONFIG_FEATURE_SYSLOG is not set
>> +
>> +#
>> +# Build Options
>> +#
>> +CONFIG_CROSS_COMPILE=""
>> +CONFIG_SYSROOT=""
>> +CONFIG_EXTRA_CFLAGS=""
>> +CONFIG_EXTRA_LDFLAGS=""
>> +CONFIG_EXTRA_LDLIBS=""
>> +
>> +#
>> +# Debugging Options
>> +#
>> +# CONFIG_DEBUG is not set
>> +# CONFIG_WERROR is not set
>> +# CONFIG_NOCLEANUP is not set
>> +# CONFIG_BOOTLOADER_EBG is not set
>> +# CONFIG_UBOOT is not set
>> +# CONFIG_BOOTLOADER_NONE is not set
>> +# CONFIG_BOOTLOADER_GRUB is not set
>> +# CONFIG_DOWNLOAD is not set
>> +# CONFIG_DOWNLOAD_SSL is not set
>> +# CONFIG_CHANNEL_CURL is not set
>> +# CONFIG_HASH_VERIFY=y
>> +# CONFIG_SIGNED_IMAGES is not set
>> +# CONFIG_ENCRYPTED_IMAGES is not set
>> +# CONFIG_SURICATTA is not set
>> +# CONFIG_WEBSERVER is not set
>> +CONFIG_GUNZIP=y
>> +
>> +#
>> +# Parser Features
>> +#
>> +CONFIG_LIBCONFIG=y
>> +CONFIG_PARSERROOT=""
>> +# CONFIG_JSON is not set
>> +# CONFIG_LUAEXTERNAL is not set
>> +# CONFIG_SETEXTPARSERNAME is not set
>> +# CONFIG_SETSWDESCRIPTION is not set
>> +
>> +#
>> +# Image Handlers
>> +#
>> +CONFIG_RAW=y
>> +# CONFIG_LUASCRIPTHANDLER is not set
>> +# CONFIG_SHELLSCRIPTHANDLER is not set
>> +# CONFIG_HANDLER_IN_LUA is not set
>> +# CONFIG_EMBEDDED_LUA_HANDLER is not set
>> +# CONFIG_EMBEDDED_LUA_HANDLER_SOURCE is not set
>> +CONFIG_ARCHIVE=y
>> +# CONFIG_REMOTE_HANDLER is not set
>> +# CONFIG_SWUFORWARDER_HANDLER is not set
>> +# CONFIG_BOOTLOADERHANDLER is not set
>> diff --git 
>> a/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet 
>> b/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
>> new file mode 100644
>> index 0000000..8e3688c
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
>> @@ -0,0 +1,3 @@
>> +CONFIG_BOOTLOADER_NONE=n
>> +CONFIG_BOOTLOADER_EBG=y
>> +CONFIG_BOOTLOADERHANDLER=y
>> diff --git 
>> a/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet 
>> b/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
>> new file mode 100644
>> index 0000000..b39f9df
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
>> @@ -0,0 +1,2 @@
>> +CONFIG_LUA=y
>> +CONFIG_LUAPKG="lua53"
>> diff --git 
>> a/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet 
>> b/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
>> new file mode 100644
>> index 0000000..b4a2de8
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
>> @@ -0,0 +1,4 @@
>> +CONFIG_LUASCRIPTHANDLER=y
>> +CONFIG_HANDLER_IN_LUA=y
>> +CONFIG_EMBEDDED_LUA_HANDLER=y
>> +CONFIG_EMBEDDED_LUA_HANDLER_SOURCE="swupdate_handlers.lua"
>> diff --git 
>> a/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet 
>> b/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
>> new file mode 100644
>> index 0000000..eab98dd
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
>> @@ -0,0 +1 @@
>> +CONFIG_MTD=y
>> diff --git 
>> a/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet 
>> b/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
>> new file mode 100644
>> index 0000000..6b5832a
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
>> @@ -0,0 +1,3 @@
>> +CONFIG_UBOOT=y
>> +CONFIG_UBOOT_FWENV="/etc/fw_env.config"
>> +CONFIG_BOOTLOADERHANDLER=y
>> diff --git 
>> a/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet 
>> b/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
>> new file mode 100644
>> index 0000000..d1c7732
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
>> @@ -0,0 +1,6 @@
>> +CONFIG_UBIVOL=y
>> +CONFIG_UBIATTACH=y
>> +CONFIG_UBIBLACKLIST=""
>> +CONFIG_UBIWHITELIST=""
>> +CONFIG_UBIVIDOFFSET=0
>> +CONFIG_CFI=y
>> diff --git a/recipes-core/swupdate/files/swupdate_handlers.lua 
>> b/recipes-core/swupdate/files/swupdate_handlers.lua
>> new file mode 100644
>> index 0000000..c9b9962
>> --- /dev/null
>> +++ b/recipes-core/swupdate/files/swupdate_handlers.lua
>> @@ -0,0 +1,449 @@
>> +--[[
>> +
>> +    Round-robin Image and File Handler.
>> +
>> +    Copyright (C) 2019, Siemens AG
>> +
>> +    Author: Christian Storm <christian.storm@siemens.com>
>> +
>> +    SPDX-License-Identifier: GPL-2.0-or-later
>> +
>> +    An `sw-description` file using these handlers may look like:
>> +        software =
>> +        {
>> +            version = "0.1.0";
>> +            images: ({
>> +                filename = "rootfs.ext4";
>> +                device = "sda4,sda5";
>> +                type = "roundrobin";
>> +                compressed = false;
>> +            });
>> +            files: ({
>> +                filename = "vmlinuz";
>> +                path = "vmlinuz";
>> +                type = "kernelfile";
>> +                device = "sda2,sda3";
>> +                filesystem = "vfat";
>> +            },
>> +            {
>> +                filename = "initrd.img";
>> +                path = "initrd.img";
>> +                type = "kernelfile";
>> +                device = "sda2,sda3";
>> +                filesystem = "vfat";
>> +            });
>> +        }
>> +
>> +    The semantics is as follows: Instead of having a fixed target 
>> device,
>> +    the 'roundrobin' image handler calculates the target device by 
>> parsing
>> +    /proc/cmdline, matching the root=<device> kernel parameter 
>> against its
>> +    'device' attribute's list of devices, and sets the actual target
>> +    device to the next 'device' attribute list entry in a round-robin
>> +    manner. The actual flashing is done via chain-calling another 
>> handler,
>> +    defaulting to the "raw" handler.
>> +
>> +    The 'kernelfile' file handler reuses the 'roundrobin' handler's 
>> target
>> +    device calculation by reading the actual target device from the same
>> +    index into its 'device' attribute's list of devices. The actual 
>> placing
>> +    of files into this partition is done via chain-calling another 
>> handler,
>> +    defaulting to the "rawfile" handler.
>> +
>> +    In the above example, if /dev/sda4 is currently booted according to
>> +    /proc/cmdline, /dev/sda5 will be flashed and the vmlinuz and 
>> initrd.img
>> +    files will be placed on /dev/sda3. If /dev/sda5 is booted, /dev/sda4
>> +    will be flashed and the vmlinuz and initrd.img files are placed on
>> +    /dev/sda2.
>> +    In addition to "classical" device nodes as in this example, 
>> partition
>> +    UUIDs as reported, e.g., by `blkid -s PARTUUID` are also supported.
>> +    UBI volumes are supported as well by specifying a CSV list of
>> +    ubi<number>:<label> items.
>> +
>> +    Configuration is done via an INI-style configuration file located at
>> +    /etc/swupdate.handler.ini or via compiled-in configuration (by
>> +    embedding the Lua handler script into the SWUpdate binary via using
>> +    CONFIG_EMBEDDED_LUA_HANDLER), the latter having precedence over the
>> +    former. See the example configuration below.
>> +    If uncommenting this example block, it will take precedence over any
>> +    /etc/swupdate.handler.ini configuration file.
>> +
>> +    The chain-called handlers can either be specified in the 
>> configuration,
>> +    i.e., a static run-time setting, or via the 'chainhandler' 
>> property of
>> +    an 'image' or 'file' section in the sw-description, with the latter
>> +    taking precedence over the former, e.g.,
>> +        ...
>> +        images: ({
>> +                filename = "rootfs.ext4";
>> +                device = "sda4,sda5";
>> +                type = "roundrobin";
>> +                properties: {
>> +                    chainhandler = "myraw";
>> +                };
>> +            });
>> +        ...
>> +    Such a sw-description fragment will chain-call the imaginary "myraw"
>> +    handler regardless of what's been configured in the compiled-in 
>> or the
>> +    configuration file.
>> +    When chain-calling the "rdiff_image" handler, its 'rdiffbase' 
>> property
>> +    is subject to round-robin as well, i.e., the 'rdiffbase' property is
>> +    expected to be a CSV list as for the 'device' property, and the 
>> actual
>> +    'rdiffbase' property value is calculated following the same 
>> round-robin
>> +    calculation mechanism stated above prior to chain-calling the actual
>> +    "rdiff_image" handler, e.g.,
>> +        images: ({
>> +                filename = "rootfs.ext4";
>> +                type = "roundrobin";
>> +                device = "sda4,sda5";
>> +                properties: {
>> +                    chainhandler = "rdiff_image";
>> +                    rdiffbase="sda1,sda2";
>> +                };
>> +            });
>> +    will set the 'rdiffbase' property to /dev/sda2 (/dev/sda1) if 
>> /dev/sda4
>> +    (/dev/sda5) is the currently booted root file system according to
>> +    /proc/cmdline parsing.
>> +
>> +]]
>> +
>> +
>> +local configuration = [[
>> +[bootloader]
>> +# Required: bootloader name, uboot and ebg currently supported.
>> +name=ebg
>> +# Required: bootloader-specific key-value pairs, e.g., for ebg:
>> +kernelname=linux.signed.efi
>> +# For relying on FAT labels, prefix bootlabels with 'L:', e.g., L:BOOT0.
>> +# For using custom labels, i.e., relying on the contents of an EFILABEL
>> +# file within the partition, prefix it with 'C:', e.g., C:BOOT0.
>> +bootlabel={ "C:BOOT0:", "C:BOOT1:" }
>> +
>> +# Optional: handler to chain-call for the 'roundrobin' handler,
>> +# defaulting to 'raw'
>> +[roundrobin]
>> +chainhandler=raw
>> +
>> +# Optional: handler to chain-call for the 'kernelfile' handler,
>> +# defaulting to 'rawfile'
>> +[kernelfile]
>> +chainhandler=rawfile
>> +]]
>> +
>> +-- Default configuration file, tried if no compiled-in config is 
>> available.
>> +local cfgfile = "/etc/swupdate.handler.ini"
>> +
>> +-- Table holding the configuration.
>> +local config = {}
>> +
>> +-- Mandatory configuration [section] and keys
>> +local BOOTLOADERCFG = {
>> +    ebg   = {
>> +        bootloader = {"name", "bootlabel", "kernelname"}
>> +    },
>> +    -- TODO fill with mandatory U-Boot configuration
>> +    uboot = {
>> +        bootloader = {"name"}
>> +    }
>> +}
>> +
>> +-- enum-alikes to make code more readable
>> +local BOOTLOADER = { EBG = "ebg", UBOOT = "uboot" }
>> +local PARTTYPE   = { UUID = 1, PLAIN = 2, UBI = 3 }
>> +
>> +-- Target table describing the target device the image is to be/has 
>> been flashed to.
>> +local rrtarget = {
>> +    size = function(self)
>> +        local _size = 0
>> +        for index in pairs(self) do _size = _size + 1 end
>> +        return _size - 1
>> +    end
>> +}
>> +
>> +-- Helper function parsing CSV fields of a struct img_type such as
>> +-- the "device" fields or the "rdiffbase" property.
>> +local get_device_list = function(device_node_csv_list)
>> +    local device_list = {}
>> +    for item in device_node_csv_list:gmatch("([^,]+)") do
>> +        local device_node = item:gsub("/dev/", "")
>> +        device_list[#device_list+1] = device_node
>> +        device_list[device_node] = #device_list
>> +    end
>> +    return device_list
>> +end
>> +
>> +-- Helper function to determine device node location.
>> +local get_device_path = function(device_node)
>> +    if device_node:match("ubi%d+:%S+") then
>> +        return 0, device_node, PARTTYPE.UBI
>> +    end
>> +    local device_path = string.format("/dev/disk/by-partuuid/%s", 
>> device_node)
>> +    local file = io.open(device_path, "rb" )
>> +    if file then
>> +        file:close()
>> +        return 0, device_path, PARTTYPE.UUID
>> +    end
>> +    device_path = string.format("/dev/%s", device_node)
>> +    file = io.open(device_path, "rb" )
>> +    if file then
>> +        file:close()
>> +        return 0, device_path, PARTTYPE.PLAIN
>> +    end
>> +    swupdate.error(string.format("Cannot access target device node 
>> /dev/{,disk/by-partuuid}/%s", device_node))
>> +    return 1, nil, nil
>> +end
>> +
>> +-- Helper function parsing the INI-style configuration.
>> +local get_config = function()
>> +    -- Return configuration right away if it's already parsed.
>> +    if config ~= nil and #config > 0 then
>> +        return config
>> +    end
>> +
>> +    -- Get configuration INI-style string.
>> +    if not configuration then
>> +        swupdate.trace(string.format("No compiled-in config found, 
>> trying %s", cfgfile))
>> +        local file = io.open(cfgfile, "r" )
>> +        if not file then
>> +            swupdate.error(string.format("Cannot open config file 
>> %s", cfgfile))
>> +            return nil
>> +        end
>> +        configuration = file:read("*a")
>> +        file:close()
>> +    end
>> +    if configuration:sub(-1) ~= "\n" then
>> +        configuration=configuration.."\n"
>> +    end
>> +
>> +    -- Parse INI-style contents into config table.
>> +    local sec, key, value
>> +    for line in configuration:gmatch("(.-)\n") do
>> +        if line:match("^%[([%w%p]+)%][%s]*") then
>> +            sec = line:match("^%[([%w%p]+)%][%s]*")
>> +            config[sec] = {}
>> +        elseif sec then
>> +            key, value = line:match("^([%w%p]-)=(.*)$")
>> +            if key and value then
>> +                if tonumber(value)  then value = tonumber(value) end
>> +                if value == "true"  then value = true            end
>> +                if value == "false" then value = false           end
>> +                if value:sub(1,1) == "{" then
>> +                    local _value = {}
>> +                    for _key, _ in value:gmatch("\"(%S+)\"") do
>> +                        table.insert(_value, _key)
>> +                    end
>> +                    value = _value
>> +                end
>> +                config[sec][key] = value
>> +            else
>> +                if not line:match("^$") and not line:match("^#") then
>> +                    swupdate.warn(string.format("Syntax error, 
>> skipping '%s'", line))
>> +                end
>> +            end
>> +        else
>> +            swupdate.error(string.format("Syntax error. no [section] 
>> encountered."))
>> +            return nil
>> +        end
>> +    end
>> +
>> +    -- Check config table for mandatory key existence.
>> +    if config["bootloader"] == nil or config["bootloader"]["name"] == 
>> nil then
>> +        swupdate.error(string.format("Syntax error. no [bootloader] 
>> encountered or name= missing therein."))
>> +        return nil
>> +    end
>> +    local bcfg = BOOTLOADERCFG[config.bootloader.name]
>> +    if not bcfg then
>> +        swupdate.error(string.format("Bootloader unsupported, 
>> name=uboot|ebg missing in [bootloader]?."))
>> +        return nil
>> +    end
>> +    for sec, _ in pairs(bcfg) do
>> +        for _, key in pairs(bcfg[sec]) do
>> +            if config[sec] == nil or config[sec][key] == nil then
>> +                swupdate.error(string.format("Mandatory config key 
>> %s= in [%s] not found.", key, sec))
>> +            end
>> +        end
>> +    end
>> +
>> +    return config
>> +end
>> +
>> +-- Round-robin image handler for updating the root partition.
>> +function handler_roundrobin(image)
>> +    -- Read configuration.
>> +    if not get_config() then
>> +        swupdate.error("Cannot read configuration.")
>> +        return 1
>> +    end
>> +
>> +    -- Check if we can chain-call the handler.
>> +    local chained_handler = "raw"
>> +    if image.properties ~= nil and image.properties["chainhandler"] 
>> ~= nil then
>> +        chained_handler = image.properties["chainhandler"]
>> +    elseif config["roundrobin"] ~= nil and 
>> config["roundrobin"]["chainhandler"] ~= nil then
>> +        chained_handler = config["roundrobin"]["chainhandler"]
>> +    end
>> +    if not swupdate.handler[chained_handler] then
>> +        swupdate.error(string.format("'%s' handler not available in 
>> SWUpdate distribution.", chained_handler))
>> +        return 1
>> +    end
>> +
>> +    -- Get device list for round-robin.
>> +    local devices = get_device_list(image.device)
>> +    if #devices < 2 then
>> +        swupdate.error("Specify at least 2 devices in the device= 
>> property for 'roundrobin'.")
>> +        return 1
>> +    end
>> +
>> +    -- Check that rrtarget is unset, else a reboot may be pending.
>> +    if rrtarget:size() > 0 then
>> +        swupdate.warn("The 'roundrobin' handler has been run. Is a 
>> reboot pending?")
>> +    end
>> +
>> +    -- Determine current root device.
>> +    local file = io.open("/proc/cmdline", "r")
>> +    if not file then
>> +        swupdate.error("Cannot open /proc/cmdline.")
>> +        return 1
>> +    end
>> +    local cmdline = file:read("*l")
>> +    file:close()
>> +
>> +    local rootparam, rootdevice
>> +    for item in cmdline:gmatch("%S+") do
>> +        rootparam, rootdevice = item:match("(root=[%u=]*[/dev/]*(%S+))")
>> +        if rootparam and rootdevice then break end
>> +    end
>> +    if not rootdevice then
>> +      swupdate.error("Cannot determine current root device.")
>> +      return 1
>> +    end
>> +    swupdate.info(string.format("Current root device is: %s", 
>> rootdevice))
>> +
>> +    if not devices[rootdevice] then
>> +        swupdate.error(string.format("Current root device '%s' is not 
>> in round-robin root devices list: %s", rootdevice, 
>> image.device:gsub("/dev/", "")))
>> +        return 1
>> +    end
>> +
>> +    -- Perform round-robin calculation for target.
>> +    local err
>> +    rrtarget.index = devices[rootdevice] % #devices + 1
>> +    rrtarget.device_node = devices[rrtarget.index]
>> +    err, rrtarget.device_path, rrtarget.parttype = 
>> get_device_path(devices[rrtarget.index])
>> +    if err ~= 0 then
>> +        return 1
>> +    end
>> +    swupdate.info(string.format("Using '%s' as 'roundrobin' target 
>> via '%s' handler.", rrtarget.device_path, chained_handler))
>> +
>> +    -- If the chain-called handler is rdiff_image, adapt the 
>> rdiffbase property
>> +    if chained_handler == "rdiff_image" then
>> +        if image.properties ~= nil and image.properties["rdiffbase"] 
>> ~= nil then
>> +            local rdiffbase_devices = 
>> get_device_list(image.properties["rdiffbase"])
>> +            if #rdiffbase_devices < 2 then
>> +                swupdate.error("Specify at least 2 devices in the 
>> rdiffbase= property for 'roundrobin'.")
>> +                return 1
>> +            end
>> +            err, image.propierties["rdiffbase"], _ = 
>> get_device_path(rdiffbase_devices[rrtarget.index])
>> +            if err ~= 0 then
>> +                return 1
>> +            end
>> +            swupdate.info(string.format("Using device %s as 
>> rdiffbase.", image.properties["rdiffbase"]))
>> +        else
>> +            swupdate.error("Property 'rdiffbase' is missing in 
>> sw-description.")
>> +            return 1
>> +        end
>> +    end
>> +
>> +    -- Actually flash the partition.
>> +    local msg
>> +    image.type = chained_handler
>> +    image.device = rrtarget.device_path
>> +    err, msg = swupdate.call_handler(chained_handler, image)
>> +    if err ~= 0 then
>> +        swupdate.error(string.format("Error chain-calling '%s' 
>> handler: %s", chained_handler, (msg or "")))
>> +        return 1
>> +    end
>> +
>> +    if config.bootloader.name == BOOTLOADER.EBG then
>> +      if rootparam then
>> +        local value = cmdline:gsub(
>> +            rootparam:gsub("%-", "%%-"),
>> +            string.format("root=%s%s",
>> +                (rrtarget.parttype == PARTTYPE.PLAIN and "") or 
>> (rrtarget.parttype == PARTTYPE.UBI and "") or "PARTUUID=",
>> +                 rrtarget.parttype == PARTTYPE.PLAIN and 
>> rrtarget.device_path or devices[rrtarget.index]
>> +            )
>> +        )
>> +        swupdate.info(string.format("Setting EFI Bootguard 
>> environment: kernelparams=%s", value))
>> +        swupdate.set_bootenv("kernelparams", value)
>> +      end
>> +    elseif config.bootloader.name == BOOTLOADER.UBOOT then
>> +        -- Update U-Boot environment.
>> +        swupdate.info(string.format("Setting U-Boot environment"))
>> +        local value = rrtarget.index
>> +        swupdate.set_bootenv("swupdpart", value);
>> +    end
>> +
>> +    return 0
>> +end
>> +
>> +-- File handler for updating kernel files.
>> +function handler_kernelfile(image)
>> +    -- Check if we can chain-call the handler.
>> +    local chained_handler = "rawfile"
>> +    if image.properties ~= nil and image.properties["chainhandler"] 
>> ~= nil then
>> +        chained_handler = image.properties["chainhandler"]
>> +    elseif config["kernelfile"] ~= nil and 
>> config["kernelfile"]["chainhandler"] ~= nil then
>> +        chained_handler = config["kernelfile"]["chainhandler"]
>> +    end
>> +    if not swupdate.handler[chained_handler] then
>> +        swupdate.error(string.format("'%s' handler not available in 
>> SWUpdate distribution."), chained_handler)
>> +        return 1
>> +    end
>> +
>> +    -- Check that rrtarget is set, else the 'roundrobin' handler 
>> hasn't been run.
>> +    if rrtarget:size() == 0 then
>> +        swupdate.error("The 'roundrobin' handler hasn't been run.")
>> +        swupdate.info("Place 'roundrobin' above 'kernelfile' in 
>> sw-description.")
>> +        return 1
>> +    end
>> +
>> +    -- Get device list for round-robin.
>> +    local devices = get_device_list(image.device)
>> +    if #devices < 2 then
>> +        swupdate.error("Specify at least 2 devices in the device= 
>> property for 'kernelfile'.")
>> +        return 1
>> +    end
>> +    if rrtarget.index > #devices then
>> +        swupdate.error("Cannot map kernel partition to root partition.")
>> +        return 1
>> +    end
>> +
>> +    -- Perform round-robin indexing for target.
>> +    local err
>> +    err, image.device, _ = get_device_path(devices[rrtarget.index])
>> +    if err ~= 0 then
>> +        return 1
>> +    end
>> +    swupdate.info(string.format("Using '%s' as 'kernelfile' target 
>> via '%s' handler.", image.device, chained_handler))
>> +
>> +    -- Actually copy the 'kernelfile' files.
>> +    local msg
>> +    image.type = chained_handler
>> +    err, msg = swupdate.call_handler(chained_handler, image)
>> +    if err ~= 0 then
>> +        swupdate.error(string.format("Error chain-calling '%s' 
>> handler: %s", chained_handler, (msg or "")))
>> +        return 1
>> +    end
>> +
>> +    if config.bootloader.name == BOOTLOADER.EBG then
>> +        -- Update EFI Boot Guard environment: kernelfile
>> +        local value = string.format("%s%s", 
>> config.bootloader.bootlabel[rrtarget.index], 
>> config.bootloader.kernelname)
>> +        swupdate.info(string.format("Setting EFI Bootguard 
>> environment: kernelfile=%s", value))
>> +        swupdate.set_bootenv("kernelfile", value)
>> +    elseif config.bootloader.name == BOOTLOADER.UBOOT then
>> +        -- Update U-Boot environment.
>> +        swupdate.info(string.format("Setting U-Boot environment"))
>> +        -- TODO
>> +    end
>> +
>> +    return 0
>> +end
>> +
>> +swupdate.register_handler("roundrobin", handler_roundrobin, 
>> swupdate.HANDLER_MASK.IMAGE_HANDLER)
>> +swupdate.register_handler("kernelfile", handler_kernelfile, 
>> swupdate.HANDLER_MASK.FILE_HANDLER)
>> diff --git a/recipes-core/swupdate/swupdate.bb 
>> b/recipes-core/swupdate/swupdate.bb
>> new file mode 100644
>> index 0000000..9c58f7d
>> --- /dev/null
>> +++ b/recipes-core/swupdate/swupdate.bb
>> @@ -0,0 +1,54 @@
>> +#
>> +# CIP Core, generic profile
>> +#
>> +# Copyright (c) Siemens AG, 2020
>> +#
>> +# Authors:
>> +#  Quirin Gylstorff <quirin.gylstorff@siemens.com>
>> +#
>> +# SPDX-License-Identifier: MIT
>> +
>> +hDESCRIPTION = "swupdate utility for software updates"
>> +HOMEPAGE= "https://github.com/sbabic/swupdate"
>> +LICENSE = "GPL-2.0"
>> +LIC_FILES_CHKSUM = 
>> "file://${LAYERDIR_isar}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe" 
>>
>> +
>> +SRC_URI = 
>> "gitsm://code.siemens.com/mirror/swupdate.git;branch=master;protocol=https" 
>>
> 
> Internal mirror. You need to go back to upstream.
> 
> And do we actually need gitsm? It is not a mature feature of bitbake, 
> thus generally discouraged.
> 
> Jan
> 
I will fix that in v2
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.

View/Reply Online (#4850): https://lists.cip-project.org/g/cip-dev/message/4850
Mute This Topic: https://lists.cip-project.org/mt/75102475/4520428
Group Owner: cip-dev+owner@lists.cip-project.org
Unsubscribe: https://lists.cip-project.org/g/cip-dev/leave/8129116/1171672734/xyzzy  [patchwork-cip-dev@patchwork.kernel.org]
-=-=-=-=-=-=-=-=-=-=-=-
diff mbox series

Patch

diff --git a/classes/kconfig-snippets.bbclass b/classes/kconfig-snippets.bbclass
new file mode 100644
index 0000000..d754654
--- /dev/null
+++ b/classes/kconfig-snippets.bbclass
@@ -0,0 +1,90 @@ 
+#
+# CIP Core, generic profile
+#
+# Copyright (c) Siemens AG, 2020
+#
+# Authors:
+#  Christian Storm <christian.storm@siemens.com>
+#
+# SPDX-License-Identifier: MIT
+
+KCONFIG_SNIPPETS = ""
+
+# The following function defines the kconfig snippet system
+# with automatich debian dependency injection
+#
+# To define a feature set, the user has to define the following
+# variable to an empty string:
+#
+# KFEATURE_featurename = ""
+#
+# Then, required additions to the variables can be defined:
+#
+# KFEATURE_featurename[KCONFIG_SNIPPETS] = "file://snippet-file-name.snippet"
+# KFEATURE_featurename[SRC_URI] = "file://required-file.txt"
+# KFEATURE_featurename[DEPENDS] = "deb-pkg1 deb-pkg2 deb-pkg3"
+# KFEATURE_featurename[DEBIAN_DEPENDS] = "deb-pkg1"
+# KFEATURE_featurename[BUILD_DEB_DEPENDS] = "deb-pkg1,deb-pkg2,deb-pkg3"
+
+# The 'KCONFIG_SNIPPETS' flag gives a list of URI entries, where only
+# file:// is supported. These snippets are appended to the DEFCONFIG file.
+#
+# Features can depend on other features via the following mechanism:
+#
+# KFEATURE_DEPS[feature1] = "feature2"
+
+python () {
+    requested_features = d.getVar("KFEATURES", True) or ""
+
+    features = set(requested_features.split())
+    old_features = set()
+    feature_deps = d.getVarFlags("KFEATURE_DEPS") or {}
+    while old_features != features:
+        diff_features = old_features.symmetric_difference(features)
+        old_features = features.copy()
+        for i in diff_features:
+            features.update(feature_deps.get(i, "").split())
+
+    for f in sorted(features):
+        bb.debug(2, "Feature: " + f)
+        varname = "KFEATURE_" + f
+        dummyvar = d.getVar(varname, False)
+        if dummyvar == None:
+            bb.error("Feature var " + f + " must be defined with needed flags.")
+        else:
+            feature_flags = d.getVarFlags(varname)
+            for feature_varname in sorted(feature_flags):
+                if feature_flags.get(feature_varname, "") != "":
+                    sep = " "
+
+                    # Required to add KCONFIG_SNIPPETS to SRC_URI here,
+                    # because 'SRC_URI += "${KCONFIG_SNIPPETS}"' would
+                    # conflict with SRC_APT feature.
+                    if feature_varname == "KCONFIG_SNIPPETS":
+                        d.appendVar('SRC_URI',
+                            " " + feature_flags[feature_varname].strip())
+
+                    # BUILD_DEP_DEPENDS and DEBIAN_DEPENDS is ',' separated
+                    # Only add ',' if there is already something there
+                    if feature_varname in ["BUILD_DEB_DEPENDS",
+                                           "DEBIAN_DEPENDS"]:
+                        sep = "," if d.getVar(feature_varname) else ""
+
+                    d.appendVar(feature_varname,
+                        sep + feature_flags[feature_varname].strip())
+}
+
+# DEFCONFIG must be a predefined bitbake variable and the corresponding file
+# must exist in the WORKDIR.
+# The resulting generated config is the same file suffixed with ".gen"
+
+do_prepare_build_prepend() {
+        sh -x
+        GENCONFIG="${WORKDIR}/${DEFCONFIG}".gen
+        rm -f "$GENCONFIG"
+        cp "${WORKDIR}/${DEFCONFIG}" "$GENCONFIG"
+        for CONFIG_SNIPPET in $(echo "${KCONFIG_SNIPPETS}" | sed 's#file://##g')
+        do
+                cat ${WORKDIR}/$CONFIG_SNIPPET >> "$GENCONFIG"
+        done
+}
diff --git a/classes/swupdate-config.bbclass b/classes/swupdate-config.bbclass
new file mode 100644
index 0000000..7ce51c5
--- /dev/null
+++ b/classes/swupdate-config.bbclass
@@ -0,0 +1,76 @@ 
+#
+# CIP Core, generic profile
+#
+# Copyright (c) Siemens AG, 2020
+#
+# Authors:
+#  Christian Storm <christian.storm@siemens.com>
+#
+# SPDX-License-Identifier: MIT
+
+# This class manages the config snippets together with their dependencies
+# to build SWUpdate
+
+inherit kconfig-snippets
+
+BUILD_DEB_DEPENDS = " \
+    zlib1g-dev, debhelper, libconfig-dev, libarchive-dev, \
+    python-sphinx:native, dh-systemd, libsystemd-dev"
+
+KFEATURE_lua = ""
+KFEATURE_lua[BUILD_DEB_DEPENDS] = "liblua5.3-dev"
+KFEATURE_lua[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_lua.snippet"
+
+KFEATURE_luahandler = ""
+KFEATURE_luahandler[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_luahandler.snippet"
+KFEATURE_luahandler[SRC_URI] = "file://${SWUPDATE_LUASCRIPT}"
+
+KFEATURE_DEPS = ""
+KFEATURE_DEPS[luahandler] = "lua"
+
+KFEATURE_efibootguard = ""
+KFEATURE_efibootguard[BUILD_DEB_DEPENDS] = "efibootguard-dev"
+KFEATURE_efibootguard[DEBIAN_DEPENDS] = "efibootguard-dev"
+KFEATURE_efibootguard[DEPENDS] = "efibootguard-dev"
+KFEATURE_efibootguard[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_efibootguard.snippet"
+
+KFEATURE_mtd = ""
+KFEATURE_mtd[BUILD_DEB_DEPENDS] = "libmtd-dev"
+KFEATURE_mtd[DEPENDS] = "mtd-utils"
+KFEATURE_mtd[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_mtd.snippet"
+
+KFEATURE_ubi = ""
+KFEATURE_ubi[BUILD_DEB_DEPENDS] = "libubi-dev"
+KFEATURE_ubi[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_ubi.snippet"
+
+KFEATURE_DEPS[ubi] = "mtd"
+
+KFEATURE_u-boot = ""
+KFEATURE_u-boot[BUILD_DEB_DEPENDS] = "u-boot-${MACHINE}-dev"
+KFEATURE_u-boot[DEBIAN_DEPENDS] = "u-boot-tools"
+KFEATURE_u-boot[DEPENDS] = "${U_BOOT}"
+KFEATURE_u-boot[KCONFIG_SNIPPETS] = "file://swupdate_defconfig_u-boot.snippet"
+
+SWUPDATE_LUASCRIPT ?= "swupdate_handlers.lua"
+
+def get_bootloader_featureset(d):
+    bootloader = d.getVar("BOOTLOADER", True) or ""
+    if bootloader == "efibootguard":
+        return "efibootguard"
+    if bootloader == "u-boot":
+        return "u-boot"
+    return ""
+
+SWUPDATE_KFEATURES ??= ""
+KFEATURES = "${SWUPDATE_KFEATURES}"
+KFEATURES += "${@get_bootloader_featureset(d)}"
+
+# Astonishingly, as an anonymous python function, BOOTLOADER is always None
+# one time before it gets set. So the following must be a task.
+python do_check_bootloader () {
+    bootloader = d.getVar("BOOTLOADER", True) or "None"
+    if not bootloader in ["efibootguard", "u-boot"]:
+        bb.warn("swupdate: BOOTLOADER set to incompatible value: " + bootloader)
+}
+addtask check_bootloader before do_fetch
+
diff --git a/classes/swupdate-img.bbclass b/classes/swupdate-img.bbclass
new file mode 100644
index 0000000..a21d6ec
--- /dev/null
+++ b/classes/swupdate-img.bbclass
@@ -0,0 +1,75 @@ 
+#
+# CIP Core, generic profile
+#
+# Copyright (c) Siemens AG, 2020
+#
+# Authors:
+#  Christian Storm <christian.storm@siemens.com>
+#  Quirin Gylstorff <quirin.gylstorff@siemens.com>
+#
+# SPDX-License-Identifier: MIT
+
+SWU_IMAGE_FILE ?= "${PN}-${DISTRO}-${MACHINE}.swu"
+SWU_DESCRIPTION_FILE ?= "sw-description"
+SWU_ADDITIONAL_FILES ?= ""
+SWU_SIGNED ?= ""
+SWU_SIGNATURE_EXT ?= "sig"
+SWU_SIGNATURE_TYPE ?= "rsa"
+
+IMAGER_INSTALL += "${@'openssl' if bb.utils.to_boolean(d.getVar('SWU_SIGNED')) else ''}"
+
+do_swupdate_image[stamp-extra-info] = "${DISTRO}-${MACHINE}"
+do_swupdate_image[cleandirs] += "${WORKDIR}/swu"
+do_swupdate_image() {
+    rm -f '${DEPLOY_DIR_IMAGE}/${SWU_IMAGE_FILE}'
+    cp '${WORKDIR}/${SWU_DESCRIPTION_FILE}' '${WORKDIR}/swu/${SWU_DESCRIPTION_FILE}'
+
+    # Create symlinks for files used in the update image
+    for file in ${SWU_ADDITIONAL_FILES}; do
+        if [ -e "${WORKDIR}/$file" ]; then
+            ln -s "${WORKDIR}/$file" "${WORKDIR}/swu/$file"
+        else
+            ln -s "${DEPLOY_DIR_IMAGE}/$file" "${WORKDIR}/swu/$file"
+        fi
+    done
+
+    # Prepare for signing
+    sign='${@'x' if bb.utils.to_boolean(d.getVar('SWU_SIGNED')) else ''}'
+    if [ -n "$sign" ]; then
+        image_do_mounts
+        cp -f '${SIGN_KEY}' '${WORKDIR}/dev.key'
+        test -e '${SIGN_CRT}' && cp -f '${SIGN_CRT}' '${WORKDIR}/dev.crt'
+
+        # Fill in file check sums
+        for file in ${SWU_ADDITIONAL_FILES}; do
+            sed -i "s:$file-sha256:$(sha256sum '${WORKDIR}/swu/'$file | cut -f 1 -d ' '):g" \
+                '${WORKDIR}/swu/${SWU_DESCRIPTION_FILE}'
+        done
+    fi
+
+    cd "${WORKDIR}/swu"
+    for file in '${SWU_DESCRIPTION_FILE}' ${SWU_ADDITIONAL_FILES}; do
+        echo "$file"
+        if [ -n "$sign" -a \
+             '${SWU_DESCRIPTION_FILE}' = "$file" ]; then
+            if [ "${SWU_SIGNATURE_TYPE}" = "rsa" ]; then
+                sudo chroot ${BUILDCHROOT_DIR} /usr/bin/openssl dgst \
+                    -sha256 -sign '${PP_WORK}/dev.key' \
+                    '${PP_WORK}/swu/'"$file" \
+                        > '${WORKDIR}/swu/'"$file".'${SWU_SIGNATURE_EXT}'
+            elif [ "${SWU_SIGNATURE_TYPE}" = "cms" ]; then
+                sudo chroot ${BUILDCHROOT_DIR} /usr/bin/openssl cms \
+                    -sign -in '${PP_WORK}/swu/'"$file" \
+                    -out '${WORKDIR}/swu/'"$file".'${SWU_SIGNATURE_EXT}' \
+                    -signer '${PP_WORK}/dev.crt' \
+                    -inkey '${PP_WORK}/dev.key' \
+                    -outform DER -nosmimecap -binary
+            fi
+            echo "$file".'${SWU_SIGNATURE_EXT}'
+        fi
+    done | cpio -ovL -H crc \
+        > '${DEPLOY_DIR_IMAGE}/${SWU_IMAGE_FILE}'
+    cd -
+}
+
+addtask swupdate_image before do_build after do_copy_boot_files do_install_imager_deps do_transform_template
diff --git a/recipes-core/swupdate/files/debian/changelog.tmpl b/recipes-core/swupdate/files/debian/changelog.tmpl
new file mode 100644
index 0000000..81087d3
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/changelog.tmpl
@@ -0,0 +1,6 @@ 
+swupdate (${PV}) unstable; urgency=medium
+
+  * SWUpdate
+
+ --  Christian Storm <christian.storm@siemens.com>  Thu, 31 Jan 2019 15:23:56 +0100
+
diff --git a/recipes-core/swupdate/files/debian/compat b/recipes-core/swupdate/files/debian/compat
new file mode 100644
index 0000000..b4de394
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/compat
@@ -0,0 +1 @@ 
+11
diff --git a/recipes-core/swupdate/files/debian/control.tmpl b/recipes-core/swupdate/files/debian/control.tmpl
new file mode 100644
index 0000000..2b92850
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/control.tmpl
@@ -0,0 +1,15 @@ 
+Source: swupdate
+Section: embedded
+Priority: optional
+Maintainer: Stefano Babic <sbabic@denx.de>
+Build-Depends: ${BUILD_DEB_DEPENDS}
+Standards-Version: 4.2.1
+Homepage: http://sbabic.github.io/swupdate
+
+Package: swupdate
+Architecture: any
+Depends: ${DEBIAN_DEPENDS}
+Description: reliable way to update an embedded system
+ This project is thought to help to update an embedded system from a storage media or from network.
+ However, it should be mainly considered as a framework, where further protocols or installers
+ (in SWUpdate they are called handlers) can be easily added to the application.
diff --git a/recipes-core/swupdate/files/debian/copyright b/recipes-core/swupdate/files/debian/copyright
new file mode 100644
index 0000000..f920942
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/copyright
@@ -0,0 +1,36 @@ 
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: swupdate
+Maintainer: Stefano Babic <sbabic@denx.de>
+Source: http://github.com/sbabic/swupdate
+
+Files: *
+Copyright: 2014-2017 Stefano Babic <sbabic@denx.de>
+
+License: GPL-2 with OpenSSL exception
+ This package is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+ .
+ In addition, as a special exception, the author of this
+ program gives permission to link the code of its
+ release with the OpenSSL project's "OpenSSL" library (or
+ with modified versions of it that use the same license as
+ the "OpenSSL" library), and distribute the linked
+ executables. You must obey the GNU General Public
+ License in all respects for all of the code used other
+ than "OpenSSL".  If you modify this file, you may extend
+ this exception to your version of the file, but you are
+ not obligated to do so.  If you do not wish to do so,
+ delete this exception statement from your version.
+ .
+ This package is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>
+ .
+ On Debian systems, the complete text of the GNU General
+ Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
diff --git a/recipes-core/swupdate/files/debian/rules.tmpl b/recipes-core/swupdate/files/debian/rules.tmpl
new file mode 100755
index 0000000..54cca57
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/rules.tmpl
@@ -0,0 +1,30 @@ 
+#!/usr/bin/make -f
+
+ifneq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
+export CROSS_COMPILE=$(DEB_HOST_GNU_TYPE)-
+export CC=$(DEB_HOST_GNU_TYPE)-gcc
+export LD=$(DEB_HOST_GNU_TYPE)-gcc
+endif
+
+export DH_VERBOSE = 1
+
+export DEB_BUILD_MAINT_OPTIONS = hardening=+bindnow
+
+documentation: configure
+	make man
+
+configure:
+	make ${DEFCONFIG}
+
+build: documentation configure
+	dh $@
+
+%:
+	echo $@
+	dh $@
+
+override_dh_installchangelogs:
+	true
+
+override_dh_installdocs:
+	true
diff --git a/recipes-core/swupdate/files/debian/swupdate.examples b/recipes-core/swupdate/files/debian/swupdate.examples
new file mode 100644
index 0000000..c257b75
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/swupdate.examples
@@ -0,0 +1,2 @@ 
+examples/configuration
+examples/description
diff --git a/recipes-core/swupdate/files/debian/swupdate.install b/recipes-core/swupdate/files/debian/swupdate.install
new file mode 100644
index 0000000..8957cc6
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/swupdate.install
@@ -0,0 +1,2 @@ 
+swupdate usr/bin
+swupdate.cfg /etc
diff --git a/recipes-core/swupdate/files/debian/swupdate.manpages b/recipes-core/swupdate/files/debian/swupdate.manpages
new file mode 100644
index 0000000..c3438e0
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/swupdate.manpages
@@ -0,0 +1,5 @@ 
+doc/build/man/swupdate.1
+doc/build/man/client.1
+doc/build/man/sendtohawkbit.1
+doc/build/man/hawkbitcfg.1
+doc/build/man/progress.1
diff --git a/recipes-core/swupdate/files/debian/swupdate.tmpfile b/recipes-core/swupdate/files/debian/swupdate.tmpfile
new file mode 100644
index 0000000..4743672
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/swupdate.tmpfile
@@ -0,0 +1,2 @@ 
+X /tmp/datadst
+X /tmp/scripts
diff --git a/recipes-core/swupdate/files/debian/watch b/recipes-core/swupdate/files/debian/watch
new file mode 100644
index 0000000..bc4c53e
--- /dev/null
+++ b/recipes-core/swupdate/files/debian/watch
@@ -0,0 +1,12 @@ 
+# Example watch control file for uscan
+# Rename this file to "watch" and then you can run the "uscan" command
+# to check for upstream updates and more.
+# See uscan(1) for format
+
+# Compulsory line, this is a version 4 file
+version=4
+
+# GitHub hosted projects
+opts="filenamemangle="s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%<project>-$1.tar.gz%" \
+   https://github.com/<user>/swupdate/tags \
+   (?:.*?/)?v?(\d[\d.]*)\.tar\.gz debian uupdate
diff --git a/recipes-core/swupdate/files/postinst b/recipes-core/swupdate/files/postinst
new file mode 100644
index 0000000..f15ac10
--- /dev/null
+++ b/recipes-core/swupdate/files/postinst
@@ -0,0 +1,2 @@ 
+#!/bin/sh
+deb-systemd-helper enable swupdate.socket || true
diff --git a/recipes-core/swupdate/files/swupdate.cfg b/recipes-core/swupdate/files/swupdate.cfg
new file mode 100644
index 0000000..e0222f1
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate.cfg
@@ -0,0 +1,6 @@ 
+globals :
+{
+    verbose = true;
+    loglevel = 10;
+    syslog = false;
+};
diff --git a/recipes-core/swupdate/files/swupdate.service.example b/recipes-core/swupdate/files/swupdate.service.example
new file mode 100644
index 0000000..d0b821e
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate.service.example
@@ -0,0 +1,11 @@ 
+[Unit]
+Description=SWUpdate daemon
+Documentation=https://github.com/sbabic/swupdate
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/swupdate -f /etc/swupdate.cfg
+KillMode=mixed
+
+[Install]
+WantedBy=multi-user.target
diff --git a/recipes-core/swupdate/files/swupdate.socket.example b/recipes-core/swupdate/files/swupdate.socket.example
new file mode 100644
index 0000000..2b75671
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate.socket.example
@@ -0,0 +1,11 @@ 
+[Unit]
+Description=SWUpdate socket listener
+Documentation=https://github.com/sbabic/swupdate
+Documentation=https://sbabic.github.io/swupdate
+
+[Socket]
+ListenStream=/tmp/sockinstctrl
+ListenStream=/tmp/swupdateprog
+
+[Install]
+WantedBy=sockets.target
diff --git a/recipes-core/swupdate/files/swupdate.socket.tmpl b/recipes-core/swupdate/files/swupdate.socket.tmpl
new file mode 100644
index 0000000..8e7fc1d
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate.socket.tmpl
@@ -0,0 +1,13 @@ 
+[Unit]
+Description=SWUpdate socket listener
+Documentation=https://github.com/sbabic/swupdate
+Documentation=https://sbabic.github.io/swupdate
+
+[Socket]
+SocketUser=${SWUPDATE_SOCKET_OWNER}
+SocketGroup=root
+ListenStream=/tmp/sockinstctrl
+ListenStream=/tmp/swupdateprog
+
+[Install]
+WantedBy=sockets.target
diff --git a/recipes-core/swupdate/files/swupdate_defconfig b/recipes-core/swupdate/files/swupdate_defconfig
new file mode 100644
index 0000000..9ae7cb5
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig
@@ -0,0 +1,83 @@ 
+#
+# Automatically generated file; DO NOT EDIT.
+# Swupdate Configuration
+#
+CONFIG_HAVE_DOT_CONFIG=y
+
+#
+# Swupdate Settings
+#
+
+#
+# General Configuration
+#
+# CONFIG_CURL is not set
+# CONFIG_CURL_SSL is not set
+CONFIG_SYSTEMD=y
+CONFIG_SCRIPTS=y
+# CONFIG_HW_COMPATIBILITY is not set
+CONFIG_SW_VERSIONS_FILE="/etc/sw-versions"
+
+#
+# Socket Paths
+#
+CONFIG_SOCKET_CTRL_PATH="/tmp/sockinstctrl"
+CONFIG_SOCKET_PROGRESS_PATH="/tmp/swupdateprog"
+CONFIG_SOCKET_REMOTE_HANDLER_DIRECTORY="/tmp/"
+# CONFIG_MTD is not set
+# CONFIG_LUA is not set
+# CONFIG_LUAPKG is not set
+# CONFIG_FEATURE_SYSLOG is not set
+
+#
+# Build Options
+#
+CONFIG_CROSS_COMPILE=""
+CONFIG_SYSROOT=""
+CONFIG_EXTRA_CFLAGS=""
+CONFIG_EXTRA_LDFLAGS=""
+CONFIG_EXTRA_LDLIBS=""
+
+#
+# Debugging Options
+#
+# CONFIG_DEBUG is not set
+# CONFIG_WERROR is not set
+# CONFIG_NOCLEANUP is not set
+# CONFIG_BOOTLOADER_EBG is not set
+# CONFIG_UBOOT is not set
+# CONFIG_BOOTLOADER_NONE is not set
+# CONFIG_BOOTLOADER_GRUB is not set
+# CONFIG_DOWNLOAD is not set
+# CONFIG_DOWNLOAD_SSL is not set
+# CONFIG_CHANNEL_CURL is not set
+# CONFIG_HASH_VERIFY=y
+# CONFIG_SIGNED_IMAGES is not set
+# CONFIG_ENCRYPTED_IMAGES is not set
+# CONFIG_SURICATTA is not set
+# CONFIG_WEBSERVER is not set
+CONFIG_GUNZIP=y
+
+#
+# Parser Features
+#
+CONFIG_LIBCONFIG=y
+CONFIG_PARSERROOT=""
+# CONFIG_JSON is not set
+# CONFIG_LUAEXTERNAL is not set
+# CONFIG_SETEXTPARSERNAME is not set
+# CONFIG_SETSWDESCRIPTION is not set
+
+#
+# Image Handlers
+#
+CONFIG_RAW=y
+# CONFIG_LUASCRIPTHANDLER is not set
+# CONFIG_SHELLSCRIPTHANDLER is not set
+# CONFIG_HANDLER_IN_LUA is not set
+# CONFIG_EMBEDDED_LUA_HANDLER is not set
+# CONFIG_EMBEDDED_LUA_HANDLER_SOURCE is not set
+CONFIG_ARCHIVE=y
+# CONFIG_REMOTE_HANDLER is not set
+# CONFIG_SWUFORWARDER_HANDLER is not set
+# CONFIG_BOOTLOADERHANDLER is not set
diff --git a/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet b/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
new file mode 100644
index 0000000..8e3688c
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig_efibootguard.snippet
@@ -0,0 +1,3 @@ 
+CONFIG_BOOTLOADER_NONE=n
+CONFIG_BOOTLOADER_EBG=y
+CONFIG_BOOTLOADERHANDLER=y
diff --git a/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet b/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
new file mode 100644
index 0000000..b39f9df
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig_lua.snippet
@@ -0,0 +1,2 @@ 
+CONFIG_LUA=y
+CONFIG_LUAPKG="lua53"
diff --git a/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet b/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
new file mode 100644
index 0000000..b4a2de8
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig_luahandler.snippet
@@ -0,0 +1,4 @@ 
+CONFIG_LUASCRIPTHANDLER=y
+CONFIG_HANDLER_IN_LUA=y
+CONFIG_EMBEDDED_LUA_HANDLER=y
+CONFIG_EMBEDDED_LUA_HANDLER_SOURCE="swupdate_handlers.lua"
diff --git a/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet b/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
new file mode 100644
index 0000000..eab98dd
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig_mtd.snippet
@@ -0,0 +1 @@ 
+CONFIG_MTD=y
diff --git a/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet b/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
new file mode 100644
index 0000000..6b5832a
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig_u-boot.snippet
@@ -0,0 +1,3 @@ 
+CONFIG_UBOOT=y
+CONFIG_UBOOT_FWENV="/etc/fw_env.config"
+CONFIG_BOOTLOADERHANDLER=y
diff --git a/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet b/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
new file mode 100644
index 0000000..d1c7732
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_defconfig_ubi.snippet
@@ -0,0 +1,6 @@ 
+CONFIG_UBIVOL=y
+CONFIG_UBIATTACH=y
+CONFIG_UBIBLACKLIST=""
+CONFIG_UBIWHITELIST=""
+CONFIG_UBIVIDOFFSET=0
+CONFIG_CFI=y
diff --git a/recipes-core/swupdate/files/swupdate_handlers.lua b/recipes-core/swupdate/files/swupdate_handlers.lua
new file mode 100644
index 0000000..c9b9962
--- /dev/null
+++ b/recipes-core/swupdate/files/swupdate_handlers.lua
@@ -0,0 +1,449 @@ 
+--[[
+
+    Round-robin Image and File Handler.
+
+    Copyright (C) 2019, Siemens AG
+
+    Author: Christian Storm <christian.storm@siemens.com>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+
+    An `sw-description` file using these handlers may look like:
+        software =
+        {
+            version = "0.1.0";
+            images: ({
+                filename = "rootfs.ext4";
+                device = "sda4,sda5";
+                type = "roundrobin";
+                compressed = false;
+            });
+            files: ({
+                filename = "vmlinuz";
+                path = "vmlinuz";
+                type = "kernelfile";
+                device = "sda2,sda3";
+                filesystem = "vfat";
+            },
+            {
+                filename = "initrd.img";
+                path = "initrd.img";
+                type = "kernelfile";
+                device = "sda2,sda3";
+                filesystem = "vfat";
+            });
+        }
+
+    The semantics is as follows: Instead of having a fixed target device,
+    the 'roundrobin' image handler calculates the target device by parsing
+    /proc/cmdline, matching the root=<device> kernel parameter against its
+    'device' attribute's list of devices, and sets the actual target
+    device to the next 'device' attribute list entry in a round-robin
+    manner. The actual flashing is done via chain-calling another handler,
+    defaulting to the "raw" handler.
+
+    The 'kernelfile' file handler reuses the 'roundrobin' handler's target
+    device calculation by reading the actual target device from the same
+    index into its 'device' attribute's list of devices. The actual placing
+    of files into this partition is done via chain-calling another handler,
+    defaulting to the "rawfile" handler.
+
+    In the above example, if /dev/sda4 is currently booted according to
+    /proc/cmdline, /dev/sda5 will be flashed and the vmlinuz and initrd.img
+    files will be placed on /dev/sda3. If /dev/sda5 is booted, /dev/sda4
+    will be flashed and the vmlinuz and initrd.img files are placed on
+    /dev/sda2.
+    In addition to "classical" device nodes as in this example, partition
+    UUIDs as reported, e.g., by `blkid -s PARTUUID` are also supported.
+    UBI volumes are supported as well by specifying a CSV list of
+    ubi<number>:<label> items.
+
+    Configuration is done via an INI-style configuration file located at
+    /etc/swupdate.handler.ini or via compiled-in configuration (by
+    embedding the Lua handler script into the SWUpdate binary via using
+    CONFIG_EMBEDDED_LUA_HANDLER), the latter having precedence over the
+    former. See the example configuration below.
+    If uncommenting this example block, it will take precedence over any
+    /etc/swupdate.handler.ini configuration file.
+
+    The chain-called handlers can either be specified in the configuration,
+    i.e., a static run-time setting, or via the 'chainhandler' property of
+    an 'image' or 'file' section in the sw-description, with the latter
+    taking precedence over the former, e.g.,
+        ...
+        images: ({
+                filename = "rootfs.ext4";
+                device = "sda4,sda5";
+                type = "roundrobin";
+                properties: {
+                    chainhandler = "myraw";
+                };
+            });
+        ...
+    Such a sw-description fragment will chain-call the imaginary "myraw"
+    handler regardless of what's been configured in the compiled-in or the
+    configuration file.
+    When chain-calling the "rdiff_image" handler, its 'rdiffbase' property
+    is subject to round-robin as well, i.e., the 'rdiffbase' property is
+    expected to be a CSV list as for the 'device' property, and the actual
+    'rdiffbase' property value is calculated following the same round-robin
+    calculation mechanism stated above prior to chain-calling the actual
+    "rdiff_image" handler, e.g.,
+        images: ({
+                filename = "rootfs.ext4";
+                type = "roundrobin";
+                device = "sda4,sda5";
+                properties: {
+                    chainhandler = "rdiff_image";
+                    rdiffbase="sda1,sda2";
+                };
+            });
+    will set the 'rdiffbase' property to /dev/sda2 (/dev/sda1) if /dev/sda4
+    (/dev/sda5) is the currently booted root file system according to
+    /proc/cmdline parsing.
+
+]]
+
+
+local configuration = [[
+[bootloader]
+# Required: bootloader name, uboot and ebg currently supported.
+name=ebg
+# Required: bootloader-specific key-value pairs, e.g., for ebg:
+kernelname=linux.signed.efi
+# For relying on FAT labels, prefix bootlabels with 'L:', e.g., L:BOOT0.
+# For using custom labels, i.e., relying on the contents of an EFILABEL
+# file within the partition, prefix it with 'C:', e.g., C:BOOT0.
+bootlabel={ "C:BOOT0:", "C:BOOT1:" }
+
+# Optional: handler to chain-call for the 'roundrobin' handler,
+# defaulting to 'raw'
+[roundrobin]
+chainhandler=raw
+
+# Optional: handler to chain-call for the 'kernelfile' handler,
+# defaulting to 'rawfile'
+[kernelfile]
+chainhandler=rawfile
+]]
+
+-- Default configuration file, tried if no compiled-in config is available.
+local cfgfile = "/etc/swupdate.handler.ini"
+
+-- Table holding the configuration.
+local config = {}
+
+-- Mandatory configuration [section] and keys
+local BOOTLOADERCFG = {
+    ebg   = {
+        bootloader = {"name", "bootlabel", "kernelname"}
+    },
+    -- TODO fill with mandatory U-Boot configuration
+    uboot = {
+        bootloader = {"name"}
+    }
+}
+
+-- enum-alikes to make code more readable
+local BOOTLOADER = { EBG = "ebg", UBOOT = "uboot" }
+local PARTTYPE   = { UUID = 1, PLAIN = 2, UBI = 3 }
+
+-- Target table describing the target device the image is to be/has been flashed to.
+local rrtarget = {
+    size = function(self)
+        local _size = 0
+        for index in pairs(self) do _size = _size + 1 end
+        return _size - 1
+    end
+}
+
+-- Helper function parsing CSV fields of a struct img_type such as
+-- the "device" fields or the "rdiffbase" property.
+local get_device_list = function(device_node_csv_list)
+    local device_list = {}
+    for item in device_node_csv_list:gmatch("([^,]+)") do
+        local device_node = item:gsub("/dev/", "")
+        device_list[#device_list+1] = device_node
+        device_list[device_node] = #device_list
+    end
+    return device_list
+end
+
+-- Helper function to determine device node location.
+local get_device_path = function(device_node)
+    if device_node:match("ubi%d+:%S+") then
+        return 0, device_node, PARTTYPE.UBI
+    end
+    local device_path = string.format("/dev/disk/by-partuuid/%s", device_node)
+    local file = io.open(device_path, "rb" )
+    if file then
+        file:close()
+        return 0, device_path, PARTTYPE.UUID
+    end
+    device_path = string.format("/dev/%s", device_node)
+    file = io.open(device_path, "rb" )
+    if file then
+        file:close()
+        return 0, device_path, PARTTYPE.PLAIN
+    end
+    swupdate.error(string.format("Cannot access target device node /dev/{,disk/by-partuuid}/%s", device_node))
+    return 1, nil, nil
+end
+
+-- Helper function parsing the INI-style configuration.
+local get_config = function()
+    -- Return configuration right away if it's already parsed.
+    if config ~= nil and #config > 0 then
+        return config
+    end
+
+    -- Get configuration INI-style string.
+    if not configuration then
+        swupdate.trace(string.format("No compiled-in config found, trying %s", cfgfile))
+        local file = io.open(cfgfile, "r" )
+        if not file then
+            swupdate.error(string.format("Cannot open config file %s", cfgfile))
+            return nil
+        end
+        configuration = file:read("*a")
+        file:close()
+    end
+    if configuration:sub(-1) ~= "\n" then
+        configuration=configuration.."\n"
+    end
+
+    -- Parse INI-style contents into config table.
+    local sec, key, value
+    for line in configuration:gmatch("(.-)\n") do
+        if line:match("^%[([%w%p]+)%][%s]*") then
+            sec = line:match("^%[([%w%p]+)%][%s]*")
+            config[sec] = {}
+        elseif sec then
+            key, value = line:match("^([%w%p]-)=(.*)$")
+            if key and value then
+                if tonumber(value)  then value = tonumber(value) end
+                if value == "true"  then value = true            end
+                if value == "false" then value = false           end
+                if value:sub(1,1) == "{" then
+                    local _value = {}
+                    for _key, _ in value:gmatch("\"(%S+)\"") do
+                        table.insert(_value, _key)
+                    end
+                    value = _value
+                end
+                config[sec][key] = value
+            else
+                if not line:match("^$") and not line:match("^#") then
+                    swupdate.warn(string.format("Syntax error, skipping '%s'", line))
+                end
+            end
+        else
+            swupdate.error(string.format("Syntax error. no [section] encountered."))
+            return nil
+        end
+    end
+
+    -- Check config table for mandatory key existence.
+    if config["bootloader"] == nil or config["bootloader"]["name"] == nil then
+        swupdate.error(string.format("Syntax error. no [bootloader] encountered or name= missing therein."))
+        return nil
+    end
+    local bcfg = BOOTLOADERCFG[config.bootloader.name]
+    if not bcfg then
+        swupdate.error(string.format("Bootloader unsupported, name=uboot|ebg missing in [bootloader]?."))
+        return nil
+    end
+    for sec, _ in pairs(bcfg) do
+        for _, key in pairs(bcfg[sec]) do
+            if config[sec] == nil or config[sec][key] == nil then
+                swupdate.error(string.format("Mandatory config key %s= in [%s] not found.", key, sec))
+            end
+        end
+    end
+
+    return config
+end
+
+-- Round-robin image handler for updating the root partition.
+function handler_roundrobin(image)
+    -- Read configuration.
+    if not get_config() then
+        swupdate.error("Cannot read configuration.")
+        return 1
+    end
+
+    -- Check if we can chain-call the handler.
+    local chained_handler = "raw"
+    if image.properties ~= nil and image.properties["chainhandler"] ~= nil then
+        chained_handler = image.properties["chainhandler"]
+    elseif config["roundrobin"] ~= nil and config["roundrobin"]["chainhandler"] ~= nil then
+        chained_handler = config["roundrobin"]["chainhandler"]
+    end
+    if not swupdate.handler[chained_handler] then
+        swupdate.error(string.format("'%s' handler not available in SWUpdate distribution.", chained_handler))
+        return 1
+    end
+
+    -- Get device list for round-robin.
+    local devices = get_device_list(image.device)
+    if #devices < 2 then
+        swupdate.error("Specify at least 2 devices in the device= property for 'roundrobin'.")
+        return 1
+    end
+
+    -- Check that rrtarget is unset, else a reboot may be pending.
+    if rrtarget:size() > 0 then
+        swupdate.warn("The 'roundrobin' handler has been run. Is a reboot pending?")
+    end
+
+    -- Determine current root device.
+    local file = io.open("/proc/cmdline", "r")
+    if not file then
+        swupdate.error("Cannot open /proc/cmdline.")
+        return 1
+    end
+    local cmdline = file:read("*l")
+    file:close()
+
+    local rootparam, rootdevice
+    for item in cmdline:gmatch("%S+") do
+        rootparam, rootdevice = item:match("(root=[%u=]*[/dev/]*(%S+))")
+        if rootparam and rootdevice then break end
+    end
+    if not rootdevice then
+      swupdate.error("Cannot determine current root device.")
+      return 1
+    end
+    swupdate.info(string.format("Current root device is: %s", rootdevice))
+
+    if not devices[rootdevice] then
+        swupdate.error(string.format("Current root device '%s' is not in round-robin root devices list: %s", rootdevice, image.device:gsub("/dev/", "")))
+        return 1
+    end
+
+    -- Perform round-robin calculation for target.
+    local err
+    rrtarget.index = devices[rootdevice] % #devices + 1
+    rrtarget.device_node = devices[rrtarget.index]
+    err, rrtarget.device_path, rrtarget.parttype = get_device_path(devices[rrtarget.index])
+    if err ~= 0 then
+        return 1
+    end
+    swupdate.info(string.format("Using '%s' as 'roundrobin' target via '%s' handler.", rrtarget.device_path, chained_handler))
+
+    -- If the chain-called handler is rdiff_image, adapt the rdiffbase property
+    if chained_handler == "rdiff_image" then
+        if image.properties ~= nil and image.properties["rdiffbase"] ~= nil then
+            local rdiffbase_devices = get_device_list(image.properties["rdiffbase"])
+            if #rdiffbase_devices < 2 then
+                swupdate.error("Specify at least 2 devices in the rdiffbase= property for 'roundrobin'.")
+                return 1
+            end
+            err, image.propierties["rdiffbase"], _ = get_device_path(rdiffbase_devices[rrtarget.index])
+            if err ~= 0 then
+                return 1
+            end
+            swupdate.info(string.format("Using device %s as rdiffbase.", image.properties["rdiffbase"]))
+        else
+            swupdate.error("Property 'rdiffbase' is missing in sw-description.")
+            return 1
+        end
+    end
+
+    -- Actually flash the partition.
+    local msg
+    image.type = chained_handler
+    image.device = rrtarget.device_path
+    err, msg = swupdate.call_handler(chained_handler, image)
+    if err ~= 0 then
+        swupdate.error(string.format("Error chain-calling '%s' handler: %s", chained_handler, (msg or "")))
+        return 1
+    end
+
+    if config.bootloader.name == BOOTLOADER.EBG then
+      if rootparam then
+        local value = cmdline:gsub(
+            rootparam:gsub("%-", "%%-"),
+            string.format("root=%s%s",
+                (rrtarget.parttype == PARTTYPE.PLAIN and "") or (rrtarget.parttype == PARTTYPE.UBI and "") or "PARTUUID=",
+                 rrtarget.parttype == PARTTYPE.PLAIN and rrtarget.device_path or devices[rrtarget.index]
+            )
+        )
+        swupdate.info(string.format("Setting EFI Bootguard environment: kernelparams=%s", value))
+        swupdate.set_bootenv("kernelparams", value)
+      end
+    elseif config.bootloader.name == BOOTLOADER.UBOOT then
+        -- Update U-Boot environment.
+        swupdate.info(string.format("Setting U-Boot environment"))
+        local value = rrtarget.index
+        swupdate.set_bootenv("swupdpart", value);
+    end
+
+    return 0
+end
+
+-- File handler for updating kernel files.
+function handler_kernelfile(image)
+    -- Check if we can chain-call the handler.
+    local chained_handler = "rawfile"
+    if image.properties ~= nil and image.properties["chainhandler"] ~= nil then
+        chained_handler = image.properties["chainhandler"]
+    elseif config["kernelfile"] ~= nil and config["kernelfile"]["chainhandler"] ~= nil then
+        chained_handler = config["kernelfile"]["chainhandler"]
+    end
+    if not swupdate.handler[chained_handler] then
+        swupdate.error(string.format("'%s' handler not available in SWUpdate distribution."), chained_handler)
+        return 1
+    end
+
+    -- Check that rrtarget is set, else the 'roundrobin' handler hasn't been run.
+    if rrtarget:size() == 0 then
+        swupdate.error("The 'roundrobin' handler hasn't been run.")
+        swupdate.info("Place 'roundrobin' above 'kernelfile' in sw-description.")
+        return 1
+    end
+
+    -- Get device list for round-robin.
+    local devices = get_device_list(image.device)
+    if #devices < 2 then
+        swupdate.error("Specify at least 2 devices in the device= property for 'kernelfile'.")
+        return 1
+    end
+    if rrtarget.index > #devices then
+        swupdate.error("Cannot map kernel partition to root partition.")
+        return 1
+    end
+
+    -- Perform round-robin indexing for target.
+    local err
+    err, image.device, _ = get_device_path(devices[rrtarget.index])
+    if err ~= 0 then
+        return 1
+    end
+    swupdate.info(string.format("Using '%s' as 'kernelfile' target via '%s' handler.", image.device, chained_handler))
+
+    -- Actually copy the 'kernelfile' files.
+    local msg
+    image.type = chained_handler
+    err, msg = swupdate.call_handler(chained_handler, image)
+    if err ~= 0 then
+        swupdate.error(string.format("Error chain-calling '%s' handler: %s", chained_handler, (msg or "")))
+        return 1
+    end
+
+    if config.bootloader.name == BOOTLOADER.EBG then
+        -- Update EFI Boot Guard environment: kernelfile
+        local value = string.format("%s%s", config.bootloader.bootlabel[rrtarget.index], config.bootloader.kernelname)
+        swupdate.info(string.format("Setting EFI Bootguard environment: kernelfile=%s", value))
+        swupdate.set_bootenv("kernelfile", value)
+    elseif config.bootloader.name == BOOTLOADER.UBOOT then
+        -- Update U-Boot environment.
+        swupdate.info(string.format("Setting U-Boot environment"))
+        -- TODO
+    end
+
+    return 0
+end
+
+swupdate.register_handler("roundrobin", handler_roundrobin, swupdate.HANDLER_MASK.IMAGE_HANDLER)
+swupdate.register_handler("kernelfile", handler_kernelfile, swupdate.HANDLER_MASK.FILE_HANDLER)
diff --git a/recipes-core/swupdate/swupdate.bb b/recipes-core/swupdate/swupdate.bb
new file mode 100644
index 0000000..9c58f7d
--- /dev/null
+++ b/recipes-core/swupdate/swupdate.bb
@@ -0,0 +1,54 @@ 
+#
+# CIP Core, generic profile
+#
+# Copyright (c) Siemens AG, 2020
+#
+# Authors:
+#  Quirin Gylstorff <quirin.gylstorff@siemens.com>
+#
+# SPDX-License-Identifier: MIT
+
+hDESCRIPTION = "swupdate utility for software updates"
+HOMEPAGE= "https://github.com/sbabic/swupdate"
+LICENSE = "GPL-2.0"
+LIC_FILES_CHKSUM = "file://${LAYERDIR_isar}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe"
+
+SRC_URI = "gitsm://code.siemens.com/mirror/swupdate.git;branch=master;protocol=https"
+
+SRCREV = "1a6dfbb5a0be978ac1a159758e278ab4d44167e2"
+PV = "2020.4-git+isar"
+
+DEFCONFIG := "swupdate_defconfig"
+
+SRC_URI += "file://debian \
+            file://${DEFCONFIG} \
+            file://${PN}.cfg"
+
+DEPENDS += "libubootenv"
+
+DEBIAN_DEPENDS = "${shlibs:Depends}, ${misc:Depends}"
+
+inherit dpkg
+inherit swupdate-config
+
+KFEATURES += "luahandler"
+
+S = "${WORKDIR}/git"
+
+TEMPLATE_FILES = "debian/changelog.tmpl debian/control.tmpl debian/rules.tmpl"
+TEMPLATE_VARS += "BUILD_DEB_DEPENDS DEFCONFIG DEBIAN_DEPENDS"
+
+do_prepare_build() {
+        DEBDIR=${S}/debian
+        cp -R ${WORKDIR}/debian ${S}
+
+        install -m 0644 ${WORKDIR}/${PN}.cfg ${S}/swupdate.cfg
+        install -m 0644 ${WORKDIR}/${DEFCONFIG}.gen ${S}/configs/${DEFCONFIG}
+
+        if ! grep -q "configs/${DEFCONFIG}" ${S}/.gitignore
+        then
+                echo "configs/${DEFCONFIG}" >> ${S}/.gitignore
+        fi
+        # luahandler
+        install -m 0644 ${WORKDIR}/${SWUPDATE_LUASCRIPT} ${S}
+}