meta-digi-dey: swupdate-files: add firmware update support based on files
Implement a new mechanism to allow users to create update packages based on files and folders to modify
the active system.
This is done through the new class 'swupdate-files', which creates a tar.gz update file in the image
distribution output directory containing all the files and directories to create/update. The 'tar.gz'
file is used later by the 'swu-images' recipe to generate the final SWUpdate package. The SWU package
installation process extracts the tar.gz file in the root folder ("/") of the active system.
Users can specify the list of files and directories to include in the update package using the
'SWUPDATE_FILES_LIST' variable. These files will be directly copied from the generated system rootfs and
placed in the tar.gz archive. Additionally, users can provide their custom 'tar.gz' file to use in the update
by specifying its location in the 'SWUPDATE_FILES_TARGZ_FILE' variable. In any case, all the paths to include
in the update package must be relative to "/", as it is the base directory where tar.gz file contents are
extracted.
The update process for dual boot systems sets a new u-boot flag so that active bank is not swapped once
installation is complete and system reboots.
The SWU update mechanism based on files provides a custom update script which takes care of preparing the
system for the installation process. Just like in the SWU updates based on images, users can customize this
script or override it with the 'SWUPDATE_SCRIPT' variable, specifying the location of the new script to use.
If both the 'SWUPDATE_FILES_LIST' and 'SWUPDATE_FILES_TARGZ_FILE' variables are empty, a standard images
SWUpdate package will be generated instead.
Signed-off-by: David Escalona <david.escalona@digi.com>
This commit is contained in:
parent
e5392996ed
commit
94d4bbbe9f
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)}"
|
||||
|
|
@ -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 = "<folder_path> <file_path> ..."
|
||||
#
|
||||
|
||||
# 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)}"
|
||||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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}"
|
||||
|
|
|
|||
Loading…
Reference in New Issue