Freigeben über


Firmware-Implementierungshandbuch für Komponentenfirmwareupdates (CFU)

Component Firmware Update (CFU) ist ein Protokoll und ein Prozess zum Übermitteln neuer Firmwareimages, die auf dem Zielgerät installiert werden sollen.

Hinweis

CFU ist in Windows 10, Version 2004 (Windows 10 Update vom Mai 2020) und höheren Versionen verfügbar.

CFU-Übermittlungen an die residente Firmware sind Dateipaare, eine Datei ist der Angebotsteil, die andere Datei ist der Inhaltsteil. Jede CFU-Übermittlung (jedes Angebot und jedes Inhaltspaar) muss offline erstellt werden, bevor die Übermittlung an die Firmware gesendet wird, die den CFU-Prozess implementiert.

Im Beispielquellcode der Firmware im CFU-Repository auf GitHub ist der allgemeine implementierungsunabhängige allgemeine Code für CFU in ComponentFwUpdate.centhalten. Alle anderen Dateien sind Hilfsdateien, die für die eindeutige Implementierung des Entwicklers aktualisiert oder geändert werden können.

Inhalte

Angebots- und Inhaltsteile

Das Angebot und der Inhalt bilden ein Paar von Dateien im CFU-Schema.

Der Angebotsteil ist einfach eine 16-Byte-lange Datei, die der unten beschriebenen FWUPDATE_OFFER_COMMAND-Struktur zugeordnet ist.

Der Inhaltsteil, die tatsächliche Firmware, die aktualisiert werden soll, hat das vom Endbenutzerentwickler vorgegebene Format. Der bereitgestellte CFU-Beispielcode verwendet SREC-Dateien für Firmwareinhalte.

Das Angebot ist eine 16-Byte-Sequenz. Diese Angebotsstruktur wird in die Angebotsdatei eingefügt. Es handelt sich im Wesentlichen um Binärdaten, nicht um Text, da das Angebot Bitfelder mit einer bestimmten Bedeutung enthält.

Das Angebot, das in der Datei dargestellt wird, wird dieser C-Struktur zugeordnet:

typedef struct
{
   struct
   {
       UINT8 segmentNumber;
       UINT8 reserved0 : 6;
       UINT8 forceImmediateReset : 1;
       UINT8 forceIgnoreVersion : 1;
       UINT8 componentId;
       UINT8 token;
   } componentInfo;

   UINT32 version;
   UINT32 hwVariantMask;
   struct
   {
       UINT8 protocolRevision : 4;
       UINT8 bank : 2;
       UINT8 reserved0 : 2;
       UINT8 milestone : 3;
       UINT8 reserved1 : 5;
       UINT16 productId;
   } productInfo;

} FWUPDATE_OFFER_COMMAND;

Von der niedrigen Adresse bis zur hohen Adresse ist das erste Byte des Angebots eine Segmentnummer.

  <------- 4 bytes -----------> <-- 8 bytes -->  <-------- 4 bytes --------->
+================================-=============================================+
|  15:0 7:3  2:0  7:6  5:4  3:0   31:0   31:0     7:0  7:0  7:7  6:6  5:0  7:0 |
|  PI | R1 | MS | R0 | BK | PR  | VM   | VN   |   TK | CI | FV | FR | R0 | SN  |
+================================-=============================================+

Von hoher Adresse zu niedriger Adresse:

Byte(s)    Value
---------------------------------------------------------
15:14   |  (PI)  Product ID is 2 bytes
13      |  (R1)  Reserved1 5-bit register
        |  (MS)  Milestone 3-bit register
12      |  (R2)  Reserved2 2-bit register
        |  (BK)  Bank 2-bit register
        |  (PR)  Protocol Revision  2-bit register
11:8    |  (VM)  Hardware Variant Mask 32-bit register
7:4     |  (VN)  Version 32-bit register
3       |  (TK)  Token 8-bit register
2       |  (CI)  Component ID 8-bit register
1       |  (FV)  Force Ignore Version 1-bit register
        |  (FR)  Force Immediate Reset  1-bit register
        |  (R0)  Reserved0 6-bit register
0       |  (SN)  Segment Number 8-bit register
---------------------------------------------------------

Details zur Angebotsregistrierung

Die Produkt-ID. Ein eindeutiger Produkt-ID-Wert für dieses CFU-Image kann auf dieses Feld angewendet werden.

UINT16 productID;  

Der Meilenstein der Firmware, die der Inhalt des Angebots darstellt. Meilensteine können verschiedene Versionen des HW-Builds sein, z. B. EV1-Build, EV2-Build usw. Meilensteindefinition und Wertzuweisung bleiben dem Entwickler überlassen.

UINT8 milestone : 3;

Wenn die Firmware für eine bestimmte Bank vorgesehen ist, unterstützt das 2-Bit-Feld vier Banken. Die Verwendung eines Bankregisters ist im Format des Angebots enthalten, da es Instanzen gibt, in denen die Zielgeräte bankbasierte Firmwareregionen verwenden.

Wenn dies der Fall wäre und das Angebot dazu gedacht war, eine verwendete Bank zu aktualisieren, kann die Firmware, die CFU auf dem Ziel implementiert, das Angebot ablehnen. Andernfalls kann die Firmware auf dem Ziel, das CFU implementiert, andere Maßnahmen ergreifen, wie dies erforderlich ist.

Wenn das Banking von Firmwareimages NICHT im Entwurf der Firmware des Endbenutzers liegt, ist es sinnvoll, dieses Feld zu ignorieren (auf beliebige Werte festgelegt, die bequem sind, aber der Wert im Bankfeld ist optional und hängt von der Art und Weise ab, in der die Zielfirmware CFU implementiert).

UINT8 bank : 2;

Die Protokollversion des verwendeten CFU-Protokolls ist in 4 Bits.

UINT8 protocolRevision : 4;

Die Bitmaske, die allen eindeutigen HW entspricht, auf denen dieses Firmwareimage arbeiten kann. Beispielsweise kann das Angebot bedeuten, dass es auf VerX von HW, aber nicht auf VerY von HW ausgeführt werden kann. Die Bitdefinition und die Wertzuweisung bleiben dem Entwickler überlassen.

UINT32 hwVariantMask;

Die Version der angebotenen Firmware.

UINT32 version;

Ein Bytetoken, um die benutzerspezifische Software zu identifizieren, die das Angebot macht. Dies soll zwischen Treibern und Tools unterscheiden, die beide möglicherweise versuchen, die gleiche ausgeführte Firmware zu aktualisieren. Beispielsweise kann einem CFU-Updatetreiber ein Token 0xA und einem Entwicklungsupdatetool 0xB zugewiesen werden. Jetzt kann die ausgeführte Firmware basierend auf dem Prozess, der versucht, sie zu aktualisieren, selektiv befehle annehmen oder ignorieren.

UINT8 token;

Die Komponente auf dem Gerät, die das Firmwareupdate anwenden soll.

UINT8 componentId;

Angebotsinterpretationsflags: Wenn die In-situ-Firmware Versionskonflikt ignoriert (älter als neuer), legen Sie das Bit so fest, dass die Version ignoriert wird.

UINT8 forceIgnoreVersion: 1;

Das Erzwingen des sofortigen Zurücksetzens wird mit einem Bit bestätigt. Wenn dieses Bit bestätigt wird, erwartet die Hostsoftware, dass die In-situ-Firmware das Gerät zurücksetzt. Die Aktionen des Zurücksetzens sind plattformspezifisch. Die Firmware des Geräts kann maßnahmen ergreifen, die Banken austauschen, um die aktuell aktualisierte Firmware zur aktiven In-Situ-Firmware zu machen. Oder nicht. Dies bleibt der Implementierung der Firmware überlassen. In der Regel wird erwartet, dass das Gerät, wenn die sofortige Zurücksetzung erzwungen wird, alles tut, was erforderlich ist, um die Firmware dazu zu bringen, dass die neue Bank aktualisiert wird die aktive Firmware, die auf dem Zielgerät ausgeführt wird.

UINT8 forceImmediateReset : 1;

Für den Fall, dass der Inhaltsteil des Angebots- und Inhaltspaars mehrere Teile des Inhalts umfasst.

UINT8 segmentNumber;

Verarbeiten von Angeboten

Die ProcessCFWUOffer-API akzeptiert zwei Argumente:

void ProcessCFWUOffer(FWUPDATE_OFFER_COMMAND* pCommand,
                     FWUPDATE_OFFER_RESPONSE* pResponse)

Gehen Sie in diesem Anwendungsfall davon aus, dass die Benutzersoftware Datenbytes an die ausgeführte Firmware sendet, dann ist die erste Meldung die Angebotsmeldung.

Die Angebotsnachricht ist eine oben beschriebene 16-Byte-Nachricht (die FWUPDATE_OFFER_COMMAND-Struktur).

Diese Angebotsmeldung ist die Daten, die von der ausgeführten Firmware verwendet werden, um das Angebot zu verdutzen.

Während der Löschung des Angebots benachrichtigt die ausgeführte Firmware den Absender, indem Felder in der FWUPDATE_OFFER_RESPONSE -Struktur aufgefüllt werden.

Interpretieren des Angebots

Die ausgeführte Firmware sollte ihren Status im CFU-Prozess nachverfolgen. Es kann bereit sein/warten, um ein Angebot anzunehmen, mitten in einer CFU-Transaktion, oder wartet darauf, Banken zwischen aktiver/inaktiver Firmware zu tauschen.

Wenn sich die ausgeführte Firmware in der Mitte einer CFU-Transaktion befindet, akzeptieren/verarbeiten Sie dieses Angebot nicht, und benachrichtigen Sie den Host entsprechend.

   if (s_currentOffer.updateInProgress)
   {
       memset(pResponse, 0, sizeof (FWUPDATE_OFFER_RESPONSE));

       pResponse->status = FIRMWARE_UPDATE_OFFER_BUSY;
       pResponse->rejectReasonCode = FIRMWARE_UPDATE_OFFER_BUSY;
       pResponse->token = token;
       return;
   }

Das Feld "Komponenten-ID" des Angebots kann verwendet werden, um der ausgeführten Firmware zu signalisieren, dass eine spezielle Aktion von der ausgeführten Firmware angefordert wird. Im CFU-Beispielcode wird vom Host ein Sonderangebotsbefehl verwendet, um die status der CFU-Engine abzurufen, unabhängig davon, ob die ausgeführte Software in der Lage ist und bereit ist, CFU-Angebote anzunehmen.

   else if (componentId == CFU_SPECIAL_OFFER_CMD)
   {
       FWUPDATE_SPECIAL_OFFER_COMMAND* pSpecialCommand =
           (FWUPDATE_SPECIAL_OFFER_COMMAND*)pCommand;
       if (pSpecialCommand->componentInfo.commandCode == CFU_SPECIAL_OFFER_GET_STATUS)
       {
           memset(pResponse, 0, sizeof (FWUPDATE_OFFER_RESPONSE));

           pResponse->status = FIRMWARE_UPDATE_OFFER_COMMAND_READY;
           pResponse->token = token;
           return;
       }
   }

Schließlich wird überprüft, ob ein Banktausch aussteht. Der Banktausch bezieht sich auf die Firmware, die die Informationen darüber speichert, ob sie noch im Prozess des Wechsels von der aktiven Anwendung zum neu heruntergeladenen Image ist oder nicht.

Wie und wo der Bankwechsel durchgeführt wird, ist eine implementierungsspezifische Aufgabe für die eingebettete Firmware. Das CFU-Protokoll und der CFU-Prozess ermöglichen den Austausch von Informationen zwischen der Remotebenutzeranwendung, die die CFU durchführt, und der ausgeführten In-situ-Firmware.

   else if (s_bankSwapPending)
   {
       memset(pResponse, 0, sizeof (FWUPDATE_OFFER_RESPONSE));

       pResponse->status = FIRMWARE_UPDATE_OFFER_REJECT;
       pResponse->rejectReasonCode = FIRMWARE_UPDATE_OFFER_SWAP_PENDING;
       pResponse->token = token;
       return;
   }

Wenn schließlich der Status der ausgeführten Firmware nicht ausgelastet ist und die componentId kein spezieller Befehl ist und kein Bankwechsel aussteht, können wir dieses Angebot verarbeiten.

Die Verarbeitung eines Angebots umfasst, aber nicht beschränkt auf die folgenden vier Schritte:

Schritt 1: Bankcheck

Überprüfen Sie die Bank der laufenden Anwendung bei der Bank im Angebot. Sind sie gleich oder anders?

Wenn dies der fall ist, dann lehnen Sie das Angebot ab (wir möchten das ausgeführte/aktive Image nicht überschreiben).

Andernfalls fahren Sie fort.

Schritt 2: Überprüfen von hwVariantMask

Die ausgeführte Firmware überprüft die hwVariantMask im Angebot mit der HW, in der sie ausgeführt wird. Dadurch kann die eingebettete Firmware ein Angebot ablehnen, wenn das Angebot für das Ziel ungültig ist. (Wenn sich z. B. die laufende Firmware auf einem alten HW-Build befindet und die neue angebotene Firmware für einen neueren HW-Build gedacht ist, sollte die ausgeführte Firmware dieses Angebot ablehnen)

Wenn sie ungültig ist, lehnen Sie das Angebot ab.

Andernfalls fahren Sie fort.

Schritt 3: Überprüfen der Firmwareversion

Überprüfen Sie, ob die Version des angebotenen Firmwareinhalts eine ältere oder neuere Version als die aktuelle Anwendungsfirmware aufweist.

Es bleibt der Implementierung des Benutzers überlassen, zu entscheiden, welche Firmware größer als eine andere ist und ob das Feld "forceIgnoreVersion" im Angebot verwendet werden kann. Die typische Firmwareentwicklung würde es ermöglichen, dass das Feld "forceIgnoreVersion" während der Produktentwicklung und in Debugversionen der Firmware verwendet werden kann, aber nicht erlaubt (nicht zulassen, dass ältere Firmware auf neue Firmware aktualisiert wird) in produkt-/release-Firmware.

Wenn diese Überprüfung fehlgeschlagen ist, lehnen Sie das Angebot ab.

Andernfalls fahren Sie fort.

Schritt 4: Annehmen des Angebots

Das Angebot ist gut. Akzeptieren Sie das Angebot mit einer Antwort, die auf die Art und Weise zugeschnitten ist, wie Nachrichten und status von der Firmware an die Remotebenutzeranwendung zurückgegeben werden. Die so genannte "Antwort" sind Daten (eine gepackte Datenstruktur, wie in den Demoheaderdateien dargestellt), und diese Daten werden mit den für das Gerät geeigneten Mitteln in die Benutzeranwendung geschrieben.

Verarbeiten des Inhalts

Die Verarbeitung des Inhalts ist in der Regel ein mehrstufiger Prozess. Die verschiedenen Schritte beziehen sich auf die Fähigkeit der Firmware, das Firmwareimage in Teilen zu akzeptieren, die auch als "Blöcke" von Daten bezeichnet werden. Es ist nicht immer möglich, das gesamte Bild gleichzeitig an die eingebettete Firmware zu senden, daher ist es realistisch zu erwarten, dass die Implementierung des CFU-Protokolls und des Prozesses Inhalte in kleinen Teilen akzeptiert.

In dieser Diskussion wird die Annahme verwendet, wenn der Prozess des CFU-Inhalts beschrieben wird.

Der Zustandsautomat der Inhaltsverarbeitung umfasst drei Zustände.

  1. Der Status der Verarbeitung des ersten Blocks.

  2. Der Status der Verarbeitung des letzten Blocks.

  3. Der Status der Verarbeitung eines jeden Blocks zwischen dem ersten und dem letzten.

Die Struktur des Inhaltsbefehls

Wie das Angebot verfügt der Inhalt über eine Struktur mit Feldern, die von den CFU-Algorithmen in der Demonstration verwendet werden.

typedef struct
{
   UINT8 flags;
   UINT8 length;
   UINT16 sequenceNumber;
   UINT32 address;
   UINT8 pData[MAX_UINT8];
} FWUPDATE_CONTENT_COMMAND;

Die Struktur des Inhaltsbefehls ist einfacher als die Angebotsstruktur. Der Inhalt wird als Sequenz von Bytes definiert, die in den Arbeitsspeicher geschrieben werden sollen. Die Präambel des Inhalts sind die Felder dieser Struktur:

  1. UINT8 flags Gibt an, ob der Inhalt "Block" der erste, letzte oder andere ist.

  2. UINT8 length Markiert die Länge des pData Felds. Im Democode für CFU beträgt der Grenzwert für die Größe von pData 255 Bytes. Andere Implementierungen können die maximale Größe des "Blocks" variieren.

  3. UINT16 sequenceNumber Markiert den Indexzähler, dessen Block als Inhalt übermittelt wird.

  4. UINT32 address Der Adressoffset des Blocks. In der Demonstration der CFU dieser Version enthält die Implementierung vordefinierte Informationen über die physische Adresse der einzelnen App-Regionen. Bei einer Firmwareimplementierung mit zwei Banken kann beispielsweise App1 mit der Adresse 0x9000 beginnen und App2 an der Adresse 0xA0000beginnen. Je nachdem, wie das Firmwareimage vorbereitet wurde (S-Records), kann die Adresse im SREC also entweder die physische Adresse oder ein Offset sein. In jedem Fall muss ein gemeinsames Verständnis zwischen der Vorbereitung des Inhalts und den implementierungsspezifischen Routinen der CFU-Inhaltsverarbeitung vorhanden sein, um die tatsächliche physische Adresse zu bestimmen, an der der Block im Arbeitsspeicher geschrieben werden soll. Es bleibt dem Firmwareentwickler überlassen, bewährte Methoden zu übernehmen und überprüfungen auf gültige Adressbereiche für jeden Inhaltsblog durchzuführen. Der CFU-Code veranschaulicht beispielsweise eine Überprüfung, ob App1 (gemeint für 0x9000) adressen hat, die sich mit App2 überlappen usw.

  5. UINT8 pData[MAX_UINT8] – Dies sind die unformatierten Bytes des Firmwareimageblocks. In der Benutzeranwendung wird darauf geachtet, nur Bytes in den vollständigen Bytestream des Inhaltsblocks einzufügen length .

In der Inhaltsstruktur werden keine Bitfelder gemäß der CFU-Demonstration aus dem bereitgestellten Code verwendet.

Der erste Block

Der erste Block startet den Download der Firmwareinhalte. Die ausgeführte Firmware versucht, den Block in den nicht flüchtigen Speicher zu schreiben. Natürlich enthält der Inhaltsblock Informationen darüber, wo im Arbeitsspeicher der Block geschrieben werden soll, wie viele Daten geschrieben werden sollen und andere Felder.

Jedes componentID-Zielgerät ist anders, und es gibt mehrere Methoden, um die Daten im Arbeitsspeicher zu speichern. Beispielsweise kann eine componentId das Schreiben in internen Flash erfordern, eine andere componentId kann in einen externen SPI-Flash schreiben, oder eine andere kann das I2C-Protokoll eines anderen IC verwenden, um das Image zu aktualisieren. Die in diesem Dokument enthaltene Demonstration hebt die Verwendung einer Funktion namens ICompFwUpdateBspWrite hervor, die jede eindeutige Firmware mit Kenntnis der zugrunde liegenden nicht flüchtigen Speicher-E/A-Funktionen des Ziels implementieren muss, für das sie entworfen wurde.

Jeder andere Block außer dem ersten oder letzten Block

Der Prozess der Annahme neuer Blöcke wird fortgesetzt, wenn die Benutzer-Anwendung einen anderen Block übermittelt, wieder mit Metadaten in der Nachricht für die Adresse, an der der Block geschrieben werden soll, wie viele Bytes enthalten sind, und andere Felder.

Die In-situ-Firmware würde dies wie ein erstes Blockszenario behandeln.

Es sollte jedoch beachtet werden, dass das System zu jedem Zeitpunkt den Block nicht erfassen und im Arbeitsspeicher speichern kann, es an der In-situ-Firmware liegt, mit einem Fehlercode zu reagieren.

Der letzte Block

Der letzte Block stellt nur dann eine Herausforderung dar, wenn die In-Situ-Firmware Aufgaben ausführen muss, um das Image zu überprüfen, das gerade in den Arbeitsspeicher geschrieben wurde.

Zunächst wird der letzte Block in den Arbeitsspeicher geschrieben.

Dann sollte mindestens eine CRC-Überprüfung zwischen den Daten durchgeführt werden, die bereits in den Arbeitsspeicher geschrieben wurden (vom ersten bis zum letzten Block) und dem CRC-Feld im letzten Block. Es bleibt jeder Implementierungsfirmware überlassen, zu wissen, wie der CRC für das heruntergeladene Image abgerufen werden kann.

Beachten Sie, dass die Ausführung der CRC-Überprüfung einige Zeit in Anspruch nimmt. Im Gegensatz zum normalen Ablauf der Ausführung der CFU für die Angebots- und Blockübermittlung. Die letzte Blockübermittlung, wenn sie eine CRC-Überprüfung enthält, hat eine gewisse Verzögerung, nur weil die CRC-Überprüfung möglicherweise eine große Speicherregion untersucht. Abhängig vom Zielgerät und anderen Faktoren ist dies möglicherweise kein Problem.

Wichtig

Die CRC-Überprüfung des eingehenden Bilds ist optional und kann auskommentiert werden. Es sollten jedoch bewährte Methoden angewendet werden, um diese Überprüfung zumindest zu übernehmen. Es wird dringend empfohlen, dass an dieser Stelle im CFU-Prozess andere Maßnahmen ergriffen werden, um die Integrität des heruntergeladenen Images sicherzustellen. Einige dieser Aktionen können das Überprüfen eines "signierten" Teils des Images und/oder die Überprüfung von Vertrauensketten oder andere bewährte Methoden umfassen, um ein sicheres Firmwareimage sicherzustellen. Diese werden dem Firmwareentwickler überlassen.

Bereinigen nach dem letzten Block

Nachdem der letzte Block geschrieben wurde und die CRC-Überprüfung abgeschlossen ist, reagiert die Firmware möglicherweise mit einem Fehler, wenn ein Teil der Überprüfung fehlgeschlagen ist.

Andernfalls wird erwartet, dass der CFU-Prozess in der Firmware mit einer erfolgreichen status reagiert.

Erzwungenes Zurücksetzen aktiviert

Das Flag für erzwungene Zurücksetzung im Angebot wird verwendet, um zu bestimmen, ob die MCU des Ziels einer Zurücksetzung (benutzerdefiniertes Zurücksetzen) unterzogen werden soll.

In der Regel, wenn ein Zurücksetzen erzwungen wird, besteht die Absicht darin, die MCU zu einem Zurücksetzen zu veranlassen, um die App-Bank zu wechseln. Das Aktualisieren persistenter Variablen, um anzugeben, in welchem Firmwareimage beim Zurücksetzen gestartet werden soll, bleibt dem Firmwareentwickler überlassen.