/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt OTA Update module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! \page qtota-index.html \title Over-The-Air Update Over-The-Air (OTA) update is a mechanism of distributing software updates over a wireless network without requiring physical access to a device. The target device needs to have a support for the OTA to be able to update wirelessly. The \l {http://code.qt.io/cgit/qt/qtotaupdate.git/} {Qt OTA Update} module provides tools that assist in enabling OTA update functionality in an embedded linux images build with \l {http://code.qt.io/cgit/yocto/meta-boot2qt.git/} {meta-boot2qt}. Generating new updates for OTA enabled devices is completely automated, given an ordinary linux sysroot as an input. This includes OTA updates for linux kernel, system libraries, user space applications, translation fixes, anything that is part of the sysroot. The offering includes \l {Qt OTA Update C++ Classes} {C++} and \l {Qt OTA Update QML Types} {QML} APIs to make integration with your Qt-based application a breeze. The OTA solution is based on \l {https://ostree.readthedocs.org/en/latest/} {OSTree}. If you would like to learn more about OSTree workings refer to the OSTree Documentation. There you can read about the anatomy of an OSTree repository and the deployment system, booting, and other internals of the project, as well as how OSTree compares to other update solutions. The following blog post series contain additional details on the Qt OTA Update module: \l {https://blog.qt.io/blog/2016/05/31/over-the-air-updates-part-1-introduction/} {Over-the-Air Updates, Part 1: Introduction}\br \l {https://blog.qt.io/blog/2016/06/28/over-the-air-updates-part-2-device-integration-api-and-creating-updates/} {Over-the-Air Updates, Part 2: Device Integration, API and Creating Updates}\br \l {https://blog.qt.io/blog/2016/11/11/air-updates-part-3-repository-configuration-handling/} {Over-the-Air Updates, Part 3: Repository Configuration and Handling} \section1 Features of the Update System \list \li Atomic Upgrades (all or nothing) - if an update did not fully complete, for example due to a power failure, the system will boot into an unmodified tree. The currently running tree is never modified, the update will become active on a system reboot. \li Secure - GPG signing and pinned TLS with client and server side authentication support. \li Efficient Handling of Disk Space - see the \c {/ostree} and the \c {/var} in \l {Layout of an OTA Enabled Sysroot}. \li Snapshot-based - traditional package managers (dpkg/rpm) build filesystem trees on the client side. In contrast, the primary focus of OSTree is on \e {replicating trees} composed on a server. \li Bandwidth Optimized - only the new files and the files that have changed are downloaded. When resuming from an interrupted download, only the missing files are fetched. \li Configuration Management - see the \c {/etc} in \l {Layout of an OTA Enabled Sysroot}. \li Rollback Support - atomically rollback to the previous version (tree) if something goes wrong. \li Updates Processing in Background - no unnecessary downtime for a user. \li OS updates via OTA, with support for agnostic application delivery mechanism on top. \endlist \section1 Requirements \list 1 \li Filesystem. OSTree operates in userspace, and will work on top of any Linux filesystem that supports hard and symbolic links. For OSTree to function reliably, the filesystem needs to be able to recover to a consistent state after an unclean shutdown. Any journaling or log-structured filesystem, when configured properly, is capable of such recovery. \li Boot Loader. Supported boot loaders are: U-Boot, GRUB 2. \br \endlist \section1 Quick Start Guide This guide will lead you through the full workflow of how to use the provided OTA tools. \list \li Adding the OTA capability to a device before shipping it to a customer. \li Generating an update from the new version of your product's sysroot. \li Delivering this update to a customer device via OTA. \li Securing a delivery of an update. \li Support for custom update delivery mechanisms. \endlist \section2 Installation OTA package is distributed with \l {Qt for Device Creation}. The OTA-related files are installed under \c Tools/ota directory in the main SDK install location, referred to as \c SDK_INSTALL_DIR in this guide. When executing scripts, we will refer to the current working directory as WORKDIR. We will be using the \c qt-ostree tool from the installation. To see a full list of available command line arguments run the following command: \badcode ./qt-ostree --help \endcode Instead of providing a full path to \c qt-ostree each time we refer to it in the guide, we will assume to be already in the \c SDK_INSTALL_DIR/Tools/ota/qt-ostree directory. \section2 Work on Your Product Build your product on top of the \B2Q stack, or build your own custom embedded linux image. When the image is ready for the \e {first release}, continue to the \l {Enabling OTA Functionality on a Device}. Your product should also pre-integrate an updater based on the Qt OTA Update module. You can find demo updater applications in the \c SDK_INSTALL_DIR/Tools/ota/examples/ directory. Alternatively, you can install an updater at a later point as a non-system application. \section2 Enabling OTA Functionality on a Device When preparing a device for shipping and subsequent updates are to be delivered via OTA, you first need to enable this feature in the sysroot: \list 1 \li Generate OSTree boot compatible initramfs image (skip this step if not using initramfs for booting). The device should be powered on, booted into your current product (the sysroot to be released), and connected to a machine from which you will run the \c generate-initramfs tool. The \l {https://en.wikipedia.org/wiki/Dracut_(software)} {Dracut} framework is used to generate the initramfs image based on the currently running kernel. The image uses systemd as an init system. You can, of course, provide your own (not necessarily Dracut-based) initramfs, as long as you include the required \l {The Booting Process} {OSTree boot logic}. To generate the initramfs image, run: \badcode cd SDK_INSTALL_DIR/Tools/ota/dracut/ ./generate-initramfs \endcode This will produce an \c initramfs-${device}-${release} file in the working directory. The generated initramfs file will be needed in the later steps. \target Boot loader integration. \li Boot loader integration. OSTree maintains bootloader-independent drop-in configuration files in a format as defined by \l {https://www.freedesktop.org/wiki/Specifications/BootLoaderSpec/} {The Boot Loader Specification}. Not all boot loaders support The Boot Loader Specification, so OSTree contains code to generate native configuration files from the bootloader-independent configurations. The boot script used by your device has to be changed to use the configurations that are managed by OSTree. This will ensure that, after OTA updates or rollbacks, the correct kernel version (and corresponding boot files) will be selected at boot time. \list \li \b {U-Boot} U-Boot tools package is required. In Ubuntu, this can be installed with the following command: \badcode sudo apt-get install u-boot-tools \endcode OSTree maintains the \c uEnv.txt file, which the U-Boot environment should import. If custom changes to \c uEnv.txt are required, use the \c --uboot-env-file argument from the \c {qt-ostree} tool. The provided file will be appended to OSTree's managed \c uEnv.txt. OSTree maintains the following fields in \c uEnv.txt: \list \li \c ${kernel_image}: Path to the Linux kernel image. \li \c ${ramdisk_image}: Path to the initramfs image (optional). \li \c ${bootargs}: Parameters passed to the kernel command line. \li \c ${bootdir}: Path to other files in the \c {\boot} directory that belong to the same release and should be accessible from U-Boot (DTBs, boot scripts). \endlist An example \c uEnv.txt when booting with initramfs: \badcode kernel_image=/ostree/qt-os-590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/vmlinuz bootdir=/ostree/qt-os-590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/ ramdisk_image=/ostree/qt-os-590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/initramfs bootargs=ostree=/ostree/boot.1/qt-os/590db09c66551670019a487992f4dae9cb2067e241f7c7fefd6b3d35af55895b/0 \endcode A sample U-Boot logic that uses the imported OSTree's environment variables: \raw HTML
if ${fs}load ${dtype} ${disk}:${part} ${script} uEnv.txt ; then env import -t ${script} ${filesize} else echo "Error loading uEnv.txt" exit fi fdt_file=<device_tree_filename> ${fs}load ${dtype} ${disk}:${part} ${kernel_addr} ${kernel_image} ${fs}load ${dtype} ${disk}:${part} ${fdt_addr} ${bootdir}/${fdt_file} ${fs}load ${dtype} ${disk}:${part} ${initramfs_addr} ${ramdisk_image} # Don't overwrite bootargs set by OSTree in uEnv.txt. setenv bootargs ${bootargs} <additional_bootargs> bootz ${kernel_addr} ${initramfs_addr} ${fdt_addr}\endraw Enabling OSTree support requires minimal effort when using a default boot script as the base. A default boot script here means whatever the device is currently using for booting. The \c {qt-ostree} tool does not change the kernel image format, only the path and the file name changes. If the original script uses the \c bootm command for loading the kernel image, then the OSTree-enabled script should use \c bootm too. \li \b {GRUB 2} Whenever the boot loader configuration files need to be updated on a GRUB 2 based system, OSTree executes \c ostree-grub-generator to convert bootloader-independent configuration files into native grub.cfg format. A default script, used by the \c qt-ostree tool is \c SDK_INSTALL_DIR/Tools/ota/qt-ostree/ostree-grub-generator. You can customize this script to match your requirements and provide it to \c qt-ostree via \c --grub2-cfg-generator. The \c ostree-grub-generator file contains additional details, the script itself is about 40 lines long. \endlist You should expect to find all the files that are required for the boot process under the \c {/boot} directory. Before starting to write the boot loader integration code, you can run the \c qt-ostree tool without providing any boot loader specific files and \l {The generated sysroot} {examine} the generated sysroot (see step 3). Particularly, inspect what gets installed in the \c {/boot} directory, as this location is of special interest to the boot loader integration code. The \c {/boot} directory may contain symbolic links to files in the \c {loader/} directory (for example, \c {uEnv.txt -> loader/uEnv.txt}). It is safe to read these symbolic links, as OSTree will ensure that the link target changes atomically on system updates and rollbacks. For more examples refer to \l {Device Integration Examples}. \target The Booting Process \b {The Booting Process} OSTree includes a special \c ostree= kernel argument that points to the corresponding tree (see the \c {/ostree} in \l {Layout of an OTA Enabled Sysroot}). When not using initramfs, the kernel command will also contain the \c init= argument, pointing to the \c ostree-prepare-root binary. The same binary is used from initramfs context. The \c ostree-prepare-root binary parses the \c ostree= kernel command line argument to find the correct versioned tree. It sets up the necessary mounts, notably the read-only mount on the \c {/usr} path, and makes the versioned tree to appear as a real \c {"/"} root directory in the booted system. After \c ostree-prepare-root (run as PID 1) completes, it passes control to the real init process. In initramfs context, once \c ostree-prepare-root is done, systemd's \c initrd-switch-root.target will take over. In initramfs, \c ostree-prepare-root is used as a user space utility (as opposed to PID 1, when booting without initramfs). \li Convert your sysroot into an OTA enabled sysroot. The conversion is done using the \c qt-ostree tool. \badcode sudo ./qt-ostree \ --sysroot-image-path ${PATH_TO_SYSROOT} \ --create-ota-sysroot \ --ota-json ${OTA_METADATA} \ --initramfs ../dracut/initramfs-${device}-${release} \ --uboot-env-file ../examples/device-integration/nitrogen6x/6x_bootscript \endcode \target {The generated sysroot} The generated sysroot can be examined by mounting the \c {boot.${BOOTFS_TYPE}} and the \c {rootfs.${ROOTFS_TYPE}} filesystem images found in \c {WORKDIR}. In this guide we assume that the system is based on U-Boot boot loader. Notes on the arguments passed to \c {qt-ostree}: \list \li \b {\c --sysroot-image-path} \list \li A path to your sysroot. Binary image (\c {*.img}) and archive image (\c {*.tar.gz}) is accepted as well as a path to an extracted sysroot. \endlist \li \b {\c --create-ota-sysroot} \list \li This option tells \c qt-ostree to create a binary image that contains a bootable OTA enabled sysroot. You will have to deploy the generated image to a device; in this guide, we use an SD card as memory media (see step 4). \endlist \li \b {\c --ota-json} \list \li A JSON file containing arbitrary metadata about the system. Use OtaClient::remoteMetadata to access the entire JSON file for manual parsing. \endlist \li \b {\c --initramfs} \list \li The initramfs image that we generated in the step 1. If initramfs is not used for booting, it may be necessary to provide additional kernel command line arguments (for example, \c {--kernel-args "rootwait root=/dev/sda2"}). The kernel arguments set with \b {\c --kernel-args} are passed to the bootloader integration code. If additional kernel arguments are resolved directly from boot scripts, then \c {--kernel-args} can be omitted. \endlist \li \b {\c --uboot-env-file} \list \li A custom U-Boot boot script or \c uEnv.txt file, see \l {Boot loader integration}. This argument is optional as U-Boot environment can be stored directly on the board's persistent storage dedicated for U-boot environment, or defined when building the U-Boot binary. \endlist \endlist \li Deploy the generated OTA image to an SD card. Plug in an SD card or a reader to the development host, and use the following command to find out its device name. \badcode lsblk -d \endcode Make sure to unmount all partitions on a device. \badcode sudo umount /dev/