La mise à jour de systèmes embarqués est un processus qui nécessite robustesse et sécurité. Les sources des mises à jours doivent pouvoir être identifiées pour ne pas corrompre le système et garantir leur intégrité. Ces systèmes sont également exposés à des problèmes tels que des coupures de courant, la perte de réseau, des incompatibilités. Le système doit être capable de basculer vers une ancienne version ou de relancer une mise à jour lorsque ces événements ont lieu.
Cet article va présenter SWUpdate, un outil permettant de faire des mises à jour d’image sécurisées et robustes.
Introduction à SWUpdate
SWUpdate est un Framework permettant de faire la mise à jour d’image complète sur des cibles embarquées. Il peut tourner en tâche de fond sur la cible et surveiller l’apparition de mise à jour sur un serveur distant ou être lancé manuellement par l’utilisateur. La mise à jour se déclenche lorsqu’une nouvelle version est disponible.
SWUpdate ne gère cependant pas la vérification du bon fonctionnement de l'installation. Un schéma de mise à jour comme celui décrit dans l’article Gestion des mises à jour de Linux sous U-boot nécessite un peu plus de travail. SWUpdate ne gère que la récupération, la vérification et l'installation des modifications.
SWUpdate utilise des archives dans un format qui lui est propre : le format .swu. Un fichier .swu contient les différentes images à installer, les scripts ou fichiers nécessaires et un fichier de description de l'installation nommé sw-description. Ce fichier donne à SWUpdate toutes les informations nécessaires à la mise à jour.
Fonctionnalités proposées
Formats
SWUpdate prend en charge les formats les plus communs. On peut effectuer notamment la mise à jour des partitions Flash, UBI et cartes SD/MMC.
Les partitions UBIFS
Il est possible de redimensionner les partitions au format UBIFS durant la mise à jour, mais SWUpdate ne peut pas créer de partitions. Il est possible d'utiliser des scripts de pré-installation pour contourner cette limitation.
U-Boot
SWUpdate permet de modifier les variables de l'environnement U-Boot durant la mise à jour, soit une à une, soit par une liste de variables à modifier.
Ajout de format
Il est possible d’inclure de nouveaux formats en définissant leurs méthodes d’installation via des handlers.
Récupération de l’archive de mise à jour
L’archive de mise à jour peut être fournie à la cible via des supports amovibles ou peut être téléchargée par SWUpdate sur des serveurs distants. SWUpdate est capable de relancer un téléchargement si celui-ci échoue.
SWUpdate intègre un serveur web nommé Mongoose qui permet à un utilisateur d'uploader l'image d'installation. Il existe également un mode Suricatta qui permet d’aller scruter régulièrement un serveur distant pour vérifier la présence d’une nouvelle archive de mise à jour et de démarrer l'installation automatiquement. SWUpdate peut également renvoyer son état vers un serveur à la fin de la mise à jour.
Mise à jour
Une archive pour plusieurs cibles
Le fichier sw-description peut contenir les informations nécessaires à la mise à jour de plusieurs cibles. Ceci permet de n’avoir qu’une seule archive de mise à jour pour plusieurs cartes. SWUpdate lira le nom du matériel et la version du logiciel installé sur la cible avant de décider si une mise à jour est nécessaire et quelle variante de mise à jour utiliser.
Scripts
Il est possible de compléter la mise à jour avec des scripts Shell ou Lua qui peuvent s'exécuter avant et/ou après la mise à jour.
Sécurité
SWUpdate propose une option pour sécuriser la mise à jour. Lorsque cette option est activée, les hash des différents fichiers de l’archive de mise à jour sont calculés et ajoutés au fichier sw-description. Ce dernier est ensuite signé et la signature intégrée à l’archive. Cette fonctionnalité permet à SWUpdate de vérifier l’authenticité et l’intégrité des fichiers avant de les utiliser pour la mise à jour.
Intégration Yocto et Buildroot
SWUpdate est disponible sur Yocto via le layer meta-swupdate. SWUpdate est également disponible sous forme de recette Buildroot.
Fonctionnement
Le fichier de configuration
Avant d’installer SWUpdate sur la cible, il faut configurer ses options de compilation.
Pour cela on peut modifier directement le fichier de configuration ou utiliser la commande "make menuconfig".
[caption id="attachment_4151" align="alignnone" width="600"] Extrait des configurations SWUpdate[/caption]
Sur cette copie d'écran on constate que seule la vérification de la compatibilité est activée. Lors de la mise à jour, SWUpdate va lire la valeur se trouvant dans CONFIG_HW_COMPATIBILITY_FILE pour savoir si la cible est concernée par la mise à jour.
C’est également dans ce fichier que l’on indique à SWUpdate que l’on souhaite activer l’utilisation d’un serveur ou encore les options de sécurité.
Déroulement de la mise à jour
La mise à jour avec SWUpdate se déroule en deux étapes. Il y a d’abord la construction de l’archive de mise à jour qui est effectuée par l’opérateur sur le PC hôte, puis l’application de la mise à jour sur la cible.
Sw-description
Sw-description est un fichier indispensable au fonctionnement de SWUpdate. L'outil obtient de ce fichier toutes les informations dont il a besoin, comme par exemple la compatibilité, les cibles, les informations sur les images, les scripts, etc.
Construction
Sw-description est composé d’un ensemble de tags et de sections. Le premier tag nommé “software” contient tous les autres tags. La version de la mise à jour est obligatoire. Il est ensuite possible d’ajouter diverses sections pour spécifier ce qu’il faut mettre à jour.
software ={
version = "0.0.1" ; images: ( { filename = "rpi.ext2 " ; /* nom de l’image dans l’archive */ device = "/dev/mmcblk0p2 " ; /* destination */ } ); } |
L’exemple ci-dessus est le sw-description d’une cible dont on va mettre à jour la partition /dev/mmcblk0p2. Lors de la mise à jour, SWUpdate va vérifier que le fichier rpi.ext2 se trouve bien dans l’archive .swu, extraire ce fichier et procéder à l'écriture sur la partition /dev/mmcblk0p2.
Actuellement, il y a cinq sections possibles dans sw-description:
- images : Description des différentes images à installer
- partitions : Redimensionnement des partitions UBIFS
- files : Modification d’un fichier sur la cible
- scripts : Description des scripts de post-installation et pré-installation
- uboot : Modification des variables U-Boot
Cette page présente toutes les différentes options disponibles pour chaque section.
Récupération de l’archive
Selon les méthodes et options utilisées, SWUpdate peut :
- Télécharger l’archive lui-même et lancer la mise à jour. Ceci est possible si l’option CONFIG_DOWNLOAD est activée.
- Être lancé manuellement. La récupération de l'image est faite indépendamment de SWUpdate par l'opérateur.
- Lancer un serveur web mongoose sur lequel un utilisateur peut uploader une image de mise à jour puis la déclencher.
Application de la mise à jour
Après avoir récupéré l’archive, SWUpdate va d’abord effectuer une série de vérifications avant de lancer l’installation. Si au cours de ces vérifications il rencontre une erreur, le processus de mise à jour est interrompu. Dans notre cas, les options sécurité et vérification de la compatibilité sont considérées comme activées.
SWUpdate va donc effectuer ces opérations:
- Extraire sw-description et sw-description.sig. Ce dernier est la signature de sw-description
- Vérifier la signature de l’archive grâce à une clé qui lui a été fournie
- Vérifier le hash de chaque fichier contenu dans l’archive
- Vérifier si la mise à jour est compatible avec la cible
- Vérifier si tous les fichiers mentionnés dans sw-description sont bien présents dans l’archive
- Exécuter les scripts de pré-installation
- Modifier la taille des partitions UBIFS si nécessaire
- Installer toutes les images nécessaires à la mise à jour
- Exécuter les scripts de post-installation
- Modifier les variables u-boot si nécessaire
- Renvoyer le code de retour de la mise à jour (échec ou réussite) à l’opérateur
Exemples d’utilisation
Utilisation d’une partition de secours
Dans cet exemple nous voulons mettre à jour la partition Application de notre système et modifier une variable U-Boot. La mise à jour sera effectuée à partir de la partition Swupdate.
Principe
Nous supposons que notre système ne nous permet pas d’avoir deux copies de notre partition Application. Nous démarrons alors avec une image initrd qui contient SWUpdate. A chaque redémarrage l'initrd vérifie si une nouvelle archive de mise à jour est disponible sur le serveur distant. Si aucune archive n'est disponible, le système démarre normalement.
La plupart du temps le système se trouve dans la situation 2, où il démarre sans faire de mise à jour. La situation 1 se produit lorsqu’une mise à jour est disponible ou vient d’être faite mais a échoué. De ce fait, le système n’arrive pas à démarrer sur la partition Application. SWUpdate relance alors la mise à jour.
Pour savoir si la mise à jour a bien fonctionné, un script d’init est utilisé. Il modifiera la valeur de la variable U-Boot “verif_install”. Si la mise à jour est validée, sa valeur passera à “true” et le système démarrera sur Application. Dans le cas contraire, sa valeur reste “false” et le système est redémarré afin de relancer la mise à jour.
Construction de l’archive de mise à jour
Pour pouvoir faire notre mise à jour, il faut créer l’archive .swu. Pour cela il faut d’abord construire le fichier sw-description. Dans notre cas il contiendra l’image de la partition à mettre à jour et la nouvelle valeur de la variable U-Boot.
software = {
version = "0.0.1" ; images: ( { filename = "Application.ext4" ; device = "/dev/mmcblk0p3" ; } ); uboot: ( { name = "verif_install" ; value = "false" ; } ); } |
Dans l’archive de mise à jour, il y aura uniquement le sw-description et le fichier Application.ext4.
Pour construire l’archive de mise à jour, on peut utiliser un script de ce type :
CONTAINER_VER="0.1.0" PRODUCT_NAME="my-software" FILES="sw-description Application.ext4" for i in $FILES; do echo $i; done | cpio -ov -H crc > ${PRODUCT_NAME}_${CONTAINER_VER}.swu |
Application de la mise à jour
La mise à jour sera appliquée lorsque le système sera sur SWUpdate. Si une archive est présente sur le serveur, elle sera récupérée et SWUpdate lancera la mise à jour avec la commande:
swupdate -i <Archive>
Utilisation de la redondance
Pour cet exemple, nous voulons que notre image soit installée si la version actuelle est plus ancienne que la version de la mise à jour. De plus, la mise à jour sera sécurisée et nous utiliserons U-Boot.
Principe
Ici nous avons deux copies de la partition Rootfs. L’une des partitions n’est pas active et la mise à jour sera appliquée dessus.
Après la mise à jour, le système démarrera sur la seconde partition. Cela permet ainsi d’avoir toujours une partition valide en cas de problème.
Pour savoir sur quelle partition démarrer, nous utilisons la variable U-Boot “part”. Elle peut avoir deux valeurs, 0 ou 1. Dans le premier cas la partition en cours est Rootfs-1, dans le deuxième cas Rootfs-2.
Configuration de SWUpdate
Notre exemple nécessite que SWUpdate soit compilé avec un certain nombre d’options.
La sécurité
Avant d’intégrer SWUpdate à l’image, il faut ajouter CONFIG_SIGNED_IMAGES=y dans le fichier de configuration. Suite à cela SWUpdate n’acceptera plus les archives non authentifiées.
La version
Pour activer la vérification de la version des images, il faut ajouter CONFIG_SW_VERSION_FILE = “/etc/sw-versions” dans les configurations.
Il faut que la cible contienne le fichier sw-versions car ce dernier sera lu durant la mise à jour pour savoir si l’image doit être installée. Si les versions sont différentes ou si le fichier n’existe pas, l’image est installée.
Chaque partition du système a son propre nom et sa version. Le fichier sw-version doit contenir une ligne par partition sous la forme <nom> <version>
Construction de l’archive de mise à jour
Nous avons un bloc Rootfs dans sw-description, contenant les deux copies afin de pouvoir choisir lors de la mise à jour
software ={
version = "0.1.0" ; Rootfs = { Rootfs-1: { } ; Rootfs-2: { } ; }; } |
Dans les blocs Rootfs, nous voulons mettre à jour une image et une variable U-Boot qui indiquera sur quelle partition nous nous trouvons.
Rootfs-1: {
images: ( name = "rootfs1" ; /* Nom dans sw-versions */ version = "2016-07" ; /* Version qui sera comparée */ install-if-different = "true"; filename = "rootfs.ext4" ; device = "/dev/mmcblk0p2" ; sha256 = "b655ffd9273bb7c4a8cf570d442824e55e14285876f..." ; /* Hash de l’image */ } uboot: ( { name = "part" ; value = "0" ; } ); } |
Le fichier sw-description complet ressemble à ceci:
software ={
version = "0.1.0" ; Rootfs = { rootfs-1: { images: ( { name = "rootfs1" ; version = "2016-07" ; install-if-different = "true"; filename = "rootfs.ext4" ; device = "/dev/mmcblk0p2" ; sha256 = "b655ffd9273bb7c4a8cf570d442824e55e14285876f..." ; } uboot: ( { name = "part" ; value = "0" ; } ); } rootfs-2: { images: ( name = "rootfs1" ; version = "2016-07" ; install-if-different = true; filename = "rootfs.ext4" ; device = "/dev/mmcblk0p3" ; sha256 = "b655ffd9273bb7c4a8cf570d442824e55e14285876f..."; } uboot: ( { name = "part" ; value = "1" ; } ); }; }; } |
Application de la mise à jour
Si les partitions actives sont Rootfs-1 et que nous voulons mettre à jour le rootfs sur Rootfs-2, il faut lancer les commandes suivantes :
swupdate -k <cle publique> -e Rootfs,Rootfs-2 -i <Archive>
Utilisation avec plusieurs cibles
Pour cet exemple, nous supposons que nous voulons mettre à jour plusieurs cartes différentes avec la même archive .swu. Cependant, nous voulons mettre à jour le rootfs des Board-1 alors que, pour les autres cartes, nous voulons uniquement ajouter un fichier.
Configuration de SWUpdate
Pour pouvoir faire la mise à jour sur plusieurs cible, il faut ajouter les options CONFIG_HW_COMPATIBILITY et CONFIG_HW_COMPATIBILITY_FILE=“/etc/hwrevision” dans les configurations. Le chemin peut être modifié pour pointer sur son emplacement sur la cible.
Le fichier hwrevision est similaire à sw-version, à la différence près qu’il ne contient qu’une seule ligne sous la forme <nom de la cible> <version du hardware>.
Ici, les Board-1 auront un fichier hwrevision qui contiendra “Board-1 revA”, revA étant la version du hardware.
Construction de sw-description
Le fichier sw-description pour plusieurs cibles est très proche de celui utilisé pour la redondance.
Il faut ajouter un tag avec le nom des cibles, ici Board-1.
software = {
version = "0.1.0" ; hardware-compatibility = [ revA ]; Board-1 = { Rootfs = { rootfs-1: { images: ( { filename = "rootfs.ext4"; device = "/dev/mmcblk0p2"; sha256 = "b655ffd9273bb7c4a8cf570d442824e55e14285876f..."; } ); uboot: ( { name = "part" ; value = "0" ; } ); } rootfs-2: { images: ( filename = "rootfs.ext4"; device = "/dev/mmcblk0p3"; sha256 = "b655ffd9273bb7c4a8cf570d442824e55e14285876f..." ; } ); uboot: ( { name = “part” ; value = “1” ; } ); }; }; files: ( { filename = "app.config" ; path = "/My_APP/app.config"; sha256 = "fe1b322e1e0d0216924c2fa12cf726bc1db6d2d8e1c6.... " ; } ); } |
Application de la mise à jour
Pour lancer la mise à jour sur les Board-1 si on se trouve sur rootfs-1, il faut lancer la commande:
swupdate -k <cle publique> -e Rootfs,Rootfs-2 -i <Archive>
Et sur les autres cartes:
swupdate -k <cle publique> -i <Archive>
Mise en place
Yocto
Il existe un layer SWUpdate pour Yocto nommé meta-swupdate. Son utilisation permet l'installation de SWUpdate dans une image.
Pour cela, il faut :
- Ajouter le layer meta-swupdate
- Ajouter SWUpdate à l’image
- Configurer SWUpdate (les configurations se trouvent dans le répertoire recipes-support)
- Générer l’image
Après avoir généré l’image, l’archive de mise à jour peut être construite. Pour cela, il faut :
- Construire le sw-description
- Compléter la recette nécessaire à la création de l’archive.
Le dossier recipes-extended contient un exemple de ces deux fichiers.
Lorsqu’ils sont complétés, il suffit ensuite de générer l’archive grâce à la commande :
MACHINE=<cible> bitbake <nom de la recette>
Buildroot
Il existe un package SWUpdate pour Buildroot.
Pour l’ajouter à une image, il faut :
- Ajouter SWUpdate à l’image (menu Target packages puis System tools)
- Configurer SWUpdate
Pour créer l’archive de mise à jour, il faut utiliser un post-image script ou un script extérieur comme celui vu dans la partie “Utilisation d’une partition de secours”.
Conclusion
Cet article met l’accent sur les différentes fonctionnalités de SWUpdate et son fichier de description, sw-description. Cependant toutes les possibilités de cet outil n’ont pas été détaillées ici (handlers, Suricatta …) mais leur documentation est disponible sur le site officiel.