diff --git a/meta-digi-dey/classes/dey-image.bbclass b/meta-digi-dey/classes/dey-image.bbclass index 8d43b7481..21ad42958 100644 --- a/meta-digi-dey/classes/dey-image.bbclass +++ b/meta-digi-dey/classes/dey-image.bbclass @@ -73,3 +73,6 @@ DEPENDS += "${@oe.utils.conditional('TRUSTFENCE_SIGN', '1', 'trustfence-sign-too # Do not include kernel in rootfs images PACKAGE_EXCLUDE = "kernel-image-*" + +# Create 'tar.gz' file for SWUpdate files update mechanism. +inherit swupdate-files diff --git a/meta-digi-dey/classes/swupdate-files-common.bbclass b/meta-digi-dey/classes/swupdate-files-common.bbclass new file mode 100644 index 000000000..4255d9f18 --- /dev/null +++ b/meta-digi-dey/classes/swupdate-files-common.bbclass @@ -0,0 +1,18 @@ +# Copyright (C) 2023 Digi International. +# + +# Variable used to generate the tar.gz file. Do not modify. +SWUPDATE_FILES_TARGZ_FILE_NAME = "swupdate-files.tar.gz" + +# Initialize variable to provide a custom tar.gz file containing files/dirs to install. +SWUPDATE_FILES_TARGZ_FILE ?= "" + +# Initialize variable to store the files/folders that will be part of the SWUpdate package. +SWUPDATE_FILES_LIST ?= "" + +# Checks whether SWU update is based on files or not. +def update_based_on_files(d): + return str(d.getVar('SWUPDATE_FILES_TARGZ_FILE') != "" or d.getVar('SWUPDATE_FILES_LIST') != "").lower() + +# Variable that determines if SWU update is based on files or not. +SWUPDATE_IS_FILES_UPDATE = "${@update_based_on_files(d)}" diff --git a/meta-digi-dey/classes/swupdate-files.bbclass b/meta-digi-dey/classes/swupdate-files.bbclass new file mode 100644 index 000000000..68f3b2849 --- /dev/null +++ b/meta-digi-dey/classes/swupdate-files.bbclass @@ -0,0 +1,58 @@ +# Copyright (C) 2023 Digi International. +# +# Generates a 'tar.gz' file with the files and folders to be included in the update package +# as part of discrete files SWUpdate installation process. +# +# Usage: +# +# In your "local.conf" file, fill the "SWUPDATE_FILES_LIST" variable with the list of +# files/folders to include in the SWUpdate package. Paths must be relative to "/": +# +# SWUPDATE_FILES_LIST = " ..." +# + +# Load commmon variables. +inherit swupdate-files-common + +create_swupdate_targz_file() { + local targzfile="${DEPLOY_DIR_IMAGE}/${SWUPDATE_FILES_TARGZ_FILE_NAME}" + # Clean previous versions of the file. + rm -f "${targzfile}" + + # Create the tar file including the 'sw-versions' file, as it is mandatory. + if [ "${SWUPDATE_FILES_TARGZ_FILE}" != "" ]; then + # User provides a custom tar.gz file. Copy it to distribution dir. + cp "${SWUPDATE_FILES_TARGZ_FILE}" "${targzfile}" + # Uncompress the tar file. + if ! gzip -t "${targzfile}"; then + # File is not correctly compressed, exit with error. + echo "[ERROR] File ${SWUPDATE_FILES_TARGZ_FILE} is not a valid 'tar.gz' file. Aborting..." + exit 1 + fi + gunzip "${targzfile}" + # Add the 'sw-versions' file. + tar -C "${IMAGE_ROOTFS}" -uf "${targzfile%.*}" etc/sw-versions + else + # The tar.gz file is not provided by user. Create it including the 'sw-versions' file + tar -C "${IMAGE_ROOTFS}" -cf "${targzfile%.*}" etc/sw-versions + fi + + # Iterate the list of files and folders. Add all entries directly except paths starting + # with 'mnt/linux'. Those files must be added from the 'DEPLOY_DIR_IMAGE' instead of + # 'IMAGE_ROOTFS', as they are part of the 'boot' image. + for file in ${SWUPDATE_FILES_LIST}; do + case "${file}" in + mnt/linux/*) + FILE_NAME="$(basename "${file}")" + tar -C "${DEPLOY_DIR_IMAGE}" --transform 's,^,mnt/linux/,' -uhf "${targzfile%.*}" "${FILE_NAME}" + ;; + *) + tar -C "${IMAGE_ROOTFS}" -uf "${targzfile%.*}" "${file}" + ;; + esac + done + + # Compress the tar file. + gzip "${targzfile%.*}" +} +ROOTFS_POSTPROCESS_COMMAND:append = "${@oe.utils.conditional('SWUPDATE_IS_FILES_UPDATE', 'true', ' create_swupdate_targz_file;', '', d)}" diff --git a/meta-digi-dey/recipes-digi/dualboot/dualboot/update-firmware b/meta-digi-dey/recipes-digi/dualboot/dualboot/update-firmware index 8c24e896a..2fd76f9b8 100755 --- a/meta-digi-dey/recipes-digi/dualboot/dualboot/update-firmware +++ b/meta-digi-dey/recipes-digi/dualboot/dualboot/update-firmware @@ -116,6 +116,16 @@ reboot_system() { } swap_active_system() { + # Sanity check: Some firmware updates might request not to swap + # active bank after the update. + local SWAP_BANK=$(fw_printenv -n swap_bank) + if [ "${SWAP_BANK}" = "false" ]; then + echo "[WARNING] Active system swap cancelled by update process." + # Reset variable and return without swapping. + fw_setenv swap_bank + return 0 + fi + if [ -z "${EMMCROOTFS}" ]; then fw_setenv mtdbootpart ${ALT_BOOT} fw_setenv mtdrootfspart ${ALT_ROOTFS} diff --git a/meta-digi-dey/recipes-digi/swu-images/files/sw-description-files_template b/meta-digi-dey/recipes-digi/swu-images/files/sw-description-files_template new file mode 100644 index 000000000..3cd479c14 --- /dev/null +++ b/meta-digi-dey/recipes-digi/swu-images/files/sw-description-files_template @@ -0,0 +1,52 @@ +software = +{ + version = "@@DEY_FIRMWARE_VERSION@@"; + description = "@@DESCRIPTION@@"; + + @@SWUPDATE_STORAGE_TYPE@@ = { + primary: { + files: ( + { + filename = "@@SWUPDATE_FILES_TARGZ_FILE_NAME@@"; + type = "archive"; + compressed = "zlib"; + path = "/"; + } + ); + scripts: ( + { + filename = "@@SWUPDATE_SCRIPT_NAME@@"; + type = "shellscript"; + } + ); + uboot: ( + { + name = "swap_bank"; + value = "false"; + } + ); + } + secondary: { + ref = "#./primary"; + } + single: { + files: ( + { + filename = "@@SWUPDATE_FILES_TARGZ_FILE_NAME@@"; + type = "archive"; + compressed = "zlib"; + path = "/system/"; + } + ); + scripts: ( + { + filename = "@@SWUPDATE_SCRIPT_NAME@@"; + type = "shellscript"; + } + ); + } + platform = { + ref = "#./single"; + } + }; +} diff --git a/meta-digi-dey/recipes-digi/swu-images/files/update_files.sh b/meta-digi-dey/recipes-digi/swu-images/files/update_files.sh new file mode 100755 index 000000000..9d28629ec --- /dev/null +++ b/meta-digi-dey/recipes-digi/swu-images/files/update_files.sh @@ -0,0 +1,144 @@ +#!/bin/sh +#=============================================================================== +# +# update_files +# +# Copyright (C) 2023 by Digi International Inc. +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 as published by +# the Free Software Foundation. +# +# +# !Description: SWU update files script +# +#=============================================================================== + +# Sanity check. This script should be always executed with at least one argument. +if [ $# -lt 1 ]; then + exit 1; +fi + +# Variables. +FS_TYPE="ext4" +LINUX_DEV_BLOCK="/dev/mmcblk0p1" +LINUX_MOUNT_POINT="/mnt/linux" +ROOTFS_DEV_BLOCK="/dev/mmcblk0p3" +ROOTFS_MOUNT_POINT="/system" + +# Determines whether the file system type is UBI or not. +is_ubifs() { + [ -c "/dev/ubi0" ] +} + +# Retrieves the MTD partition number corresponding to the given partition name. +# +# Args: +# $1: partition name. +# +# Returns: +# The MTD partition number corresponding to the given partition name, -1 if +# not found. +get_mtd_number() { + local mtd_line="$(sed -ne "/${1}/s,^mtd\([0-9]\+\).*,\1,g;T;p" /proc/mtd)" + echo "${mtd_line:--1}" +} + +# Creates the UBI device for the given MTD partition number. +# +# Args: +# $1: the MTD partition number to create the UBI device for. +# +# Returns: +# The created UBI device number for the given MTD partition number, -1 if error. +create_ubi_device() { + local dev_number="$(ubiattach -m "${1}" 2>/dev/null | sed -ne 's,.*device number \([0-9]\).*,\1,g;T;p' 2>/dev/null)" + echo "${dev_number:--1}" +} + +# Retrieves the UBI device number containing the given partition name. If the +# device does not exist, the method attempts to create it based on the MTD dev +# number containing the desired partition. +# +# Args: +# $1: partition name. +# +# Returns: +# The UBI device number containing the given partition name, -1 if not found. +get_ubi_device() { + local ubi_devices="$(ubinfo | grep "Present UBI devices:" | cut -d ":" -f2 | xargs | sed -e 's/,//g')" + for ubi_device in ${ubi_devices}; do + if ubinfo "/dev/${ubi_device}" -a | grep -qe "Name:.*$1"; then + echo "${ubi_device}" | tr -dc '0-9' + return 0 + fi + done + + # Look for the MTD number containing the given partition name. + local mtd_num="$(get_mtd_number "${1}")" + if [ "${mtd_num}" = "-1" ]; then + echo "-1" + return 1 + else + # Create the UBI device. + ubi_device_number="$(create_ubi_device "${mtd_num}")" + echo "${ubi_device_number}" + fi +} + +# Mounts all required partitions to perform the firmware update based on the update +# running source and file system type. +mount_partitions() { + # Determine whether the update is running from recovery partition or not. + BOOT_RECOVERY="$(fw_printenv -n boot_recovery)" + if [ "${BOOT_RECOVERY}" = "yes" ]; then + # Update is running from recovery partition. We need to mount both, + # the rootfs and the kernel partitions. To do so first determine the + # filesystem type, assume it is MMC device. + if is_ubifs; then + FS_TYPE="ubifs" + # Look for the UBI device containing 'linux' partition. + local linux_ubi_device="$(get_ubi_device linux)" + [ "${linux_ubi_device}" = "-1" ] && { echo "Unable to find UBI device containing 'linux' partition."; exit 1; } + LINUX_DEV_BLOCK="ubi${linux_ubi_device}:linux" + # Look for the UBI device containing 'rootfs' partition. + local rootfs_ubi_device="$(get_ubi_device rootfs)" + [ "${rootfs_ubi_device}" = "-1" ] && { echo "Unable to find UBI device containing 'rootfs' partition."; exit 1; } + ROOTFS_DEV_BLOCK="ubi${rootfs_ubi_device}:rootfs" + fi + # Mount 'rootfs' partition. + mkdir -p "${ROOTFS_MOUNT_POINT}" + mount -t "${FS_TYPE}" "${ROOTFS_DEV_BLOCK}" "${ROOTFS_MOUNT_POINT}" + # Mount 'linux' partition. + LINUX_MOUNT_POINT="${ROOTFS_MOUNT_POINT}${LINUX_MOUNT_POINT}" + mkdir -p "${LINUX_MOUNT_POINT}" + if ! is_ubifs; then + FS_TYPE="auto" + fi + mount -t "${FS_TYPE}" "${LINUX_DEV_BLOCK}" "${LINUX_MOUNT_POINT}" + else + # Update is running from the active system. In this case the 'rootfs' and 'linux' + # partitions are already mounted; however 'linux' partition is in R/O mode. Just + # remount 'linux' partition as R/W. + mount -o remount,rw "${LINUX_MOUNT_POINT}" + fi +} + +# Called just before installation process starts. +if [ "${1}" = "preinst" ]; then + mount_partitions + + # TODO: Execute custom code here. For example: + # - Mount additional devices/partitions. + # - Stop services/process before installing files. +fi + +# Called just after installation process ends. +if [ "${1}" = "postinst" ]; then + : + + # TODO: Execute custom code here. For example: + # - Clean directories. + # - Post-process files. +fi diff --git a/meta-digi-dey/recipes-digi/swu-images/swu.inc b/meta-digi-dey/recipes-digi/swu-images/swu.inc index e5cc9e701..e521f03db 100644 --- a/meta-digi-dey/recipes-digi/swu-images/swu.inc +++ b/meta-digi-dey/recipes-digi/swu-images/swu.inc @@ -7,15 +7,17 @@ LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/GPL-2.0-only;md5=801f80980d171d SRC_URI = " \ file://sw-description-images_template \ + file://sw-description-files_template \ file://sw-description-uboot \ file://swupdate_uboot_nand.sh \ file://swupdate_uboot_mmc.sh \ file://image_template_mmc \ file://image_template_nand \ file://update_images.sh \ + file://update_files.sh \ " -inherit swupdate +inherit swupdate swupdate-files-common IMAGE_DEPENDS = "${@get_baseimg_pn(d)}" @@ -31,7 +33,7 @@ SWUPDATE_STORAGE_TYPE = "${@oe.utils.conditional('STORAGE_MEDIA', 'mmc', 'mmc', INHIBIT_SWUPDATE_ADD_SRC_URI = "true" SWUPDATE_IMAGES = " \ - ${IMG_NAME} \ + ${@oe.utils.ifelse(d.getVar('SWUPDATE_IS_FILES_UPDATE') == 'true', '${SWUPDATE_FILES_TARGZ_FILE_NAME}', '${IMG_NAME}')} \ ${@oe.utils.ifelse(d.getVar('SWUPDATE_UBOOTIMG') == 'true', '${UBOOT_PREFIX}', '')} \ ${@oe.utils.ifelse(d.getVar('SWUPDATE_UBOOTIMG') == 'true', '${SWUPDATE_UBOOT_SCRIPT}', '')} \ " @@ -73,7 +75,7 @@ ROOTFS_TYPE = "${@bb.utils.contains('IMAGE_FEATURES', 'read-only-rootfs', 'squas IMAGE_TEMPLATE_FILE = "${@bb.utils.contains('STORAGE_MEDIA', 'mmc', '${WORKDIR}/image_template_mmc', '${WORKDIR}/image_template_nand', d)}" # Update script. -SWUPDATE_SCRIPT ?= "${WORKDIR}/update_images.sh" +SWUPDATE_SCRIPT ?= "${@oe.utils.vartrue('SWUPDATE_IS_FILES_UPDATE', '${WORKDIR}/update_files.sh', '${WORKDIR}/update_images.sh', d)}" SWUPDATE_SCRIPT_NAME = "${@os.path.basename(d.getVar('SWUPDATE_SCRIPT'))}" SWUPDATE_IMAGES += " ${SWUPDATE_SCRIPT_NAME}" @@ -91,6 +93,8 @@ fill_description() { sed -i -e "s,##UBOOTIMG_NAME##,${UBOOT_PREFIX}-${MACHINE}${UBOOT_EXT},g" "${WORKDIR}/sw-description" sed -i -e "s,##SWUPDATE_UBOOT_SCRIPT##,${SWUPDATE_UBOOT_SCRIPT},g" "${WORKDIR}/sw-description" sed -i -e "s,##UBOOTIMG_OFFSET##,${UBOOTIMG_OFFSET},g" "${WORKDIR}/sw-description" + elif [ "${SWUPDATE_IS_FILES_UPDATE}" = "true" ]; then + cp ${WORKDIR}/sw-description-files_template ${WORKDIR}/sw-description else cp ${WORKDIR}/sw-description-images_template ${WORKDIR}/sw-description fi @@ -100,6 +104,10 @@ fill_description() { cp "${SWUPDATE_SCRIPT}" "${DEPLOY_DIR_IMAGE}" fi + if [ "${SWUPDATE_IS_FILES_UPDATE}" = "true" ]; then + return 0 + fi + # Build image names. BOOT_IMAGE_NAME="${IMG_NAME}-${MACHINE}${BOOTFS_EXT}" ROOTFS_IMAGE_NAME="${IMG_NAME}-${MACHINE}${ROOTFS_EXT}"