Ein Angebot von

Automatisches Firmware-Update für Embedded-Linux

| Autor / Redakteur: Willi Flühmann * / Sebastian Gerstl

Zunehmende Vernetzung, leichte Bedienbarkeit, Sicherheit, Zukunftssicherheit: All diese und weitere Aspekte sind gute Argumente dafür, auch Embedded Systeme durch regelmäßige Firmware-Updates aktuell zu halten. Aber wie lässt sichein automatisches Firmware-Update für Embedded-Linux leicht und unkompliziert umsetzen?
Zunehmende Vernetzung, leichte Bedienbarkeit, Sicherheit, Zukunftssicherheit: All diese und weitere Aspekte sind gute Argumente dafür, auch Embedded Systeme durch regelmäßige Firmware-Updates aktuell zu halten. Aber wie lässt sichein automatisches Firmware-Update für Embedded-Linux leicht und unkompliziert umsetzen? (Bild: Clipdealer)

In Embedded Systemen wird die Notwendigkeit, die Firmware des Systems durch Updates auf dem neuesten Stand zu halten, immer wichtiger – nicht nur aus Sicherheitsgründen. Doch wie lässt sich ein automatisches Firmware-Update für Embedded-Linux elegant und auch für Nutzer leicht handhabbar implementieren?

Bei Embedded-Systemen wird Vernetzung zunehmend wichtiger, um deren Nutzen weiter zu steigern und neue Anwendungsgebiete zu erschließen. Oft bedeutet dies eine Anbindung an die Cloud und damit neben höherer Komplexität eine zwingende Unterstützung gängiger Kommunikationsprotokolle. Der einfachste Weg dazu ist die Verwendung einer Plattform wie Linux mit einem reichhaltigen Ökosystem von fertigen, standardisierten Software-Komponenten. In einem solchen Umfeld ist die Möglichkeit, ein System zuverlässig durch Updates zu aktualisieren, sehr wichtig geworden:

  • Komplexität macht Systeme fehleranfällig: Regelmäßige Nachbesserungen werden notwendig.
  • Vernetzung macht Systeme direkt angreifbar: Sicherheitslücken müssen rasch behoben werden können.
  • Standardisierte Software-Komponenten ermöglichen großflächige Angriffe: Komponenten müssen regelmäßig auf neuere Versionen aktualisiert werden.
  • Schnelllebige Protokolle und Services: Für die Erhaltung der Kompatibilität sind manchmal Anpassungen an der Kommunikation nötig.

Die Kooperation mit den Benutzern bei der Durchführung von Updates ist erfahrungsgemäß nicht einfach. Ist ein Update mit vielen manuellen Schritten, längerem Betriebsunterbruch oder gar technischem Vorwissen verbunden, dann sinkt die Bereitschaft zum Update und viele Systeme bleiben auf einem alten Stand.

Das Versprechen, zwischendurch mal neue nützliche Features auszuliefern, kann die Motivation beim Benutzer verbessern, ist aber dann eine langfristige Verpflichtung mit zusätzlichem Aufwand. Zudem gibt es einige Embedded-Systeme, die ohne eigenes UI irgendwo unzugänglich verbaut sind und damit weniger gut laufend gewartet werden können. Deshalb drängt sich ein Updatemechanismus auf, der so komfortabel und automatisch wie möglich abläuft.

Update in Teilen oder als Ganzes?

Bei einem Embedded-System gibt es im Gegensatz zu einem Desktop-System oft höhere Ansprüche an Zuverlässigkeit und Stabilität. Es wird ein robuster und umfassender Update-Mechanismus erwartet. Ein Paket-Manager, der Software-Komponenten individuell aktualisiert, erfüllt diese Erwartungen typischerweise nicht. Es fehlt vor allem eine Möglichkeit für grundlegende Aktualisierungen (z.B. größere Änderungen an der Verzeichnisstruktur, großer Sprung in der Linux-Version) und deren atomare Durchführung.

Weiter ist es wichtig, dass die Version der Software-Komponenten gut aufeinander abgestimmt ist und dass nur wenige, gut getestete Kombinationen zum Einsatz kommen. Im Idealfall enthält das Dateisystem keine Überbleibsel mehr von der vorherigen Installation, so dass Unsicherheiten wegfallen. So bleibt die Anzahl der Konfigurationen im Feld überschaubar und die Menge möglicher Fehler ist beschränkt.

Dies alles spricht dafür, das System als Ganzes (vollständiges Image) zu aktualisieren.

Konzept für die Flash-Partitionierung

Für einen robusten Updatevorgang muss eine wichtige Entscheidung über die Aufteilung des Flashspeichers getroffen werden. Hierzu sind mehrere Konzepte denkbar (siehe Bild 1).

In unserem Projekt haben wir uns für den A/B-Ansatz entscheiden, da genügend Flashspeicher vorhanden ist. Dieser Ansatz wird mittlerweile für moderne Geräte empfohlen (z.B. "Seamless Update" ab Android Version 7.0).

Bestandteile einer Update-Lösung

Die Implementation eines automatischen Software-Updates muss auf verschiedenen Ebenen geschehen (siehe Bild 2).

Der Aufbau von Embedded-Systemen (Bootloader, Partitionierung, vorhandene Dienste und Softwarekomponenten, Anbindung an die Cloud, usw.) ist meist für die jeweilige Applikation maßgeschneidert, auch wenn Linux verwendet wird. Es scheint schwierig zu sein, in einem solchen Umfeld eine All-in-One-Lösung für den Software-Update anzubieten, weshalb es auch kaum welche gibt.

Vergleich existierender Lösungen

Trotzdem gibt es fertige Software-Komponenten für den Update, die wenigstens einen Teil unserer Anforderungen abgedeckt haben. Die folgende Tabelle zeigt drei vorhandene Lösungen, deren Anbieter und ihre Möglichkeiten auf. Alle drei Lösungen befinden sich in aktiver Entwicklung (Stand Herbst 2018) und bieten eine Yocto-Integration und kommerziellen Support:

Mender
https://mender.io/
SWUpdate
https://sbabic.github.io/
swupdate
RAUC
https://www.rauc.io/
Entwickler Northern.tech Stefano Babic (DENX), u.a. Pengutronix, u.a.
Erster Release
(GitHub)
2017 2014 2015
Lizenz Apache GPL LGPL
Sprache Go C C
Abdeckung Komplettlösung von der Bootloader-Integration bis hin zum Cloud-Server Kernkomponente in Linux für Ausführung des Updates anhand einer einer flexiblen Definition und Scripts im Update-Image Kernkomponente in Linux für Ausführung des Updates, flexibel konfigurierbar, mehrere Konzepte unterstützt; Kommandozeilen-Tools für Image-Generierung und Debugging/Eingriffe am System
Bewertung Komplettlösung, dafür weniger flexibel da bestimmte Komponenten vorgegeben (Bootloader U-Boot, Mender-Server); weniger verbreitete Sprache Go Eher als Baukasten zu verstehen, um rund um die Kernkomponente verschiedene Bootloader oder Cloud-Anbindungen zu integrieren; erfordert viel mehr eigenen Aufwand, kann dafür gut für die eigene Umgebung maßgeschneidert werden Ähnlich zu SWUpdate, bietet aber mehr Tools rund herum und stärkerem Fokus auf Sicherheit; etwas mehr vorgegeben (abstrakteres Modell, weniger Scripting), aber dafür kompensiert mit mehr Konfigurationsoptionen

In unserem Projekt haben wir uns für SWUpdate entschieden. Auch RAUC wäre in Frage gekommen. Für die Bereitstellung und Steuerung der Updates auf Serverseite wurde in unserem Projekt aufgrund der Anforderungen eine eigene Implementation auf Basis von MQTT und HTTP gewählt.

Eine in ähnlichen Projekten oft gewählte Serverkomponente ist Eclipse hawkBit. Sowohl SWUpdate als auch RAUC bieten eine Integration mit hawkBit an.

Schrittweise zu einer fertigen Lösung (mit SWUpdate)

Beim gewählten A/B-Ansatz müssten zwei vollständige Software-Kopien im Flash Platz finden. Zur Vereinfachung werden der Kernel und der Device-Tree nicht mehr in separaten Partitionen gehalten, sondern als Dateien ins Root-Dateisystem integriert, so dass nun pro Software-Kopie genau eine Partition notwendig ist (siehe Bild 3).

Die Applikationsdaten werden in einer separaten Partition abgelegt, so dass diese bei einem Update erhalten bleiben. Da wir in unserem Projekt rohes NAND-Flash verwendet haben, wurden die Partitionen auf UBI-Volumes abgebildet (mit Ausnahme des Bootloaders) und darin das Dateisystem UBIFS verwendet.

Das Update wird als Image-Datei ausgeliefert, z.B. mit der Endung "swu". Sie enthält als komprimiertes Archiv im cpio-Format alle benötigten Dateien wie Scripts und ein Abbild der zu aktualisierenden Partition.

Weiter ist darin noch eine Datei "sw-description" enthalten, welche die auszuführen-den Aktionen für SWUpdate beschreibt. Sie könnte z.B. so aussehen:

software =
{
    version = "0.1.0";

    images: (
      {
        filename = "rootfs.ubifs";
        volume = "root2";
      }
    );

    scripts: (
      {
        filename = "finalize-update.sh";
        type = "postinstall";
      }
    );
}

Nach der Aktualisierung der betreffenden Partition wird ein Postinstall-Script ausgeführt, welches die Rolle der aktiven und inaktiven Partition vertauscht. Im Falle von UBI-Volumes kann dies ganz einfach über eine Umbenennung geschehen:

ubirename /dev/ubi0 root1 root2 root2 root1
systemctl reboot

Alle oben erwähnten Dateien (sw-description, Abbild der Partition, Postinstall-Script) werden mit folgenden Zeilen in eine Image-Update gepackt:

FILES="sw-description finalize-update.sh rootfs.unifs"
for i in $FILES;do
    echo $i;done | cpio -ov -H crc > ${IMAGE_NAME}.swu

Nächster wichtiger Punkt ist die Verteilung der Updates. Hierzu baut das Gerät eine Verbindung zum Server auf, um herauszufinden ob ein Update vorliegt. In unserem Projekt wird die Soll-Version (auf dem Server für jedes registrierte Gerät festgelegt) mit der Ist-Version (als Information irgendwo Root-Dateisystem abgelegt) verglichen. Gibt es eine Abweichung, dann wird der Updatevorgang gestartet. Sinngemäß werden beim Updatevorgang folgende Befehle ausgeführt

busybox wget -q -O /tmp/update-image.swu http://update.example.com/image-v1.swu
swupdate -i /tmp/update-image.swu

Embedded-Linux-Woche: Programm und Anmeldung Auf der Embedded-Linux-Woche in Würzburg können Sie Ihr Wissen weiter vertiefen. Prämierte Referenten geben dort Seminare für Einsteiger, Fortgeschrittene und Experten zu verschiedenen Themen der Embedded Linux-Programmierung. Mehr Informationen zu Kursen und Anmeldung finden Sie auf www.linux4embedded.de.

Es gibt noch viele weitere Punkte mehr, über die an dieser Stelle geschrieben werden könnte, aber den Rahmen dieses Beitrags sprengen würden. Dazu zählen:

  • Konfiguration des Bootloaders
  • Sicherheit
  • Rollback auf vorherige Software-Kopie bei spät auftretenden Fehlern
  • Read-only Dateisystem
  • Integration von SWUpdate in den Yocto-Build
  • Datenmigration

Gesamtaufwand

Die Umsetzung der besprochenen Lösung mit SWUpdate benötigt auf allen Ebenen zusammen je nach Ausbaustufe einen Gesamtaufwand zwischen 1 bis 3 Personenmonaten.

(Dieser Beitrag wurde mit freundlicher Genehmigung des Autors dem Tagungsband Embedded Software Engineering Kongress 2018 entnommen.)

Wie man Firmware und Dateien in Embedded-Systemen schützt

Wie man Firmware und Dateien in Embedded-Systemen schützt

18.09.17 - Um sicherzustellen, dass ein Embedded-System nur mit autorisierter Firmware startet oder autorisierte Konfigurationsdateien verwendet, müssen Authentizität und Integrität der Daten verifiziert werden. lesen

Eine Firmware-Strategie für das Internet der Dinge

Systemplattformen

Eine Firmware-Strategie für das Internet der Dinge

21.01.14 - Das Internet der Dinge ändert die Art, wie eingebettete Software für Geräte entwickelt wird. Ein stimmiger Firmware-Stack ist hier enorm hilfreich und sollte sorgfältig zusammengestellt werden. lesen

* Willi Flühmann arbeitet als Software-Entwickler und -Architekt bei Noser Engineering in Zürich.

Kommentar zu diesem Artikel abgeben

Schreiben Sie uns hier Ihre Meinung ...
(nicht registrierter User)

Zur Wahrung unserer Interessen speichern wir zusätzlich zu den o.g. Informationen die IP-Adresse. Dies dient ausschließlich dem Zweck, dass Sie als Urheber des Kommentars identifiziert werden können. Rechtliche Grundlage ist die Wahrung berechtigter Interessen gem. Art 6 Abs 1 lit. f) DSGVO.
Kommentar abschicken
copyright

Dieser Beitrag ist urheberrechtlich geschützt. Sie wollen ihn für Ihre Zwecke verwenden? Infos finden Sie unter www.mycontentfactory.de (ID: 45946745 / Open Source)