Experience Embedded

Professionelle Schulungen, Beratung und Projektunterstützung

Automatisches Firmware-Update für Embedded-Linux

Autor: Willi Flühmann, Noser Engineering AG

Beitrag - Embedded Software Engineering Kongress 2018

 

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 eines Updates 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 Linuxversion) 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 Tabelle 1 im PDF.

In unserem Projekt haben wir uns für den A/B-Ansatz entschieden, 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 Abb. 1 im PDF.

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:

 

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

Golang

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 Golang

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


Tabelle 2: Vergleich existierender Lösungen


Alle drei Lösungen befinden sich in aktiver Entwicklung (Stand Herbst 2018) und bieten eine Yocto-Integration und kommerziellen Support. In unserem Projekt haben wir uns damals für SWUpdate entschieden. Auch RAUC wäre in Frage gekommen.

Für die Bereitstellung und Steuerung der Updates auf Serverseite wurde in unsererem 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 Abb. 2 im PDF.

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 kompromiertes 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ührenden Aktionen für SWUpdate beschreibt. Sie könnte z.B. so aussehen:

 


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:

 

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

 


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, siehe Abb. im PDF.

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

  • 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

 

Schauen Sie in unserem Blog nach: https://blog.noser.com/

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.

Autor

Willi Flühmann arbeitet als Software-Entwickler und -Architekt bei Noser Engineering. Als Elektroingenieur mit 16 Jahren Erfahrung in der Software-Entwicklung blickt er auf zahlreiche Projekte im Embedded- und im Desktop-Bereich zurück. Momentan arbeitet er in Projekten im IoT-Bereich. Daneben ist er auch an Themen rund um Security interessiert.

 

Beitrag als PDF downloaden


Open Source - unsere Trainings & Coachings

Wollen Sie sich auf den aktuellen Stand der Technik bringen?

Dann informieren Sie sich hier zu Schulungen/ Seminaren/ Trainings/ Workshops und individuellen Coachings von MircoConsult zum Thema Open Source / Embedded Software Engineering.

 

Training & Coaching zu den weiteren Themen unseren Portfolios finden Sie hier.


Open Source - Fachwissen

Wertvolles Fachwissen zum Thema Open Source / Embedded Software Engineering steht hier für Sie zum kostenfreien Download bereit.

Zu den Fachinformationen

 
Fachwissen zu weiteren Themen unseren Portfolios finden Sie hier.