Condividi tramite


Guida all'implementazione del firmware CFU (Component Firmware Update)

L'aggiornamento del firmware del componente (CFU) è un protocollo e un processo per l'invio di nuove immagini del firmware da installare nel dispositivo di destinazione.

Annotazioni

CFU è disponibile in Windows 10 versione 2004 (Windows 10 maggio 2020 Update) e versioni successive.

Gli invii CFU al firmware residente sono coppie di file, un file è la parte dell'offerta, l'altro è la parte del contenuto. Ogni invio CFU (ogni offerta e coppia di contenuto) deve essere creato fuori linea prima che l'invio venga inviato al firmware che implementa il processo CFU.

Nel codice sorgente del firmware di esempio nel repository CFU su GitHub, il codice comune indipendente dall'implementazione per CFU è contenuto in ComponentFwUpdate.c. Tutti gli altri file sono file helper che possono essere aggiornati o modificati nell'implementazione univoca dello sviluppatore.

Contenuto

Le parti di offerta e i contenuti

L'offerta e il contenuto costituiscono una coppia di file nello schema CFU.

La parte dell'offerta è semplicemente un file lungo 16 byte che corrisponde alla struttura FWUPDATE_OFFER_COMMAND descritta di seguito.

La parte del contenuto, il firmware effettivo da aggiornare è nel formato determinato dallo sviluppatore dell'utente finale. Il codice di esempio CFU fornito usa i file SREC per il contenuto del firmware.

L'offerta è una sequenza di 16 byte. Questa struttura dell'offerta viene inserita nel file dell'offerta. Si tratta essenzialmente di dati binari, non di testo, perché l'offerta contiene campi di bit di significato specifico.

L'offerta rappresentata nel file corrisponde a questa struttura C:

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;

Dall'indirizzo basso all'indirizzo elevato, il primo byte dell'offerta è un numero di segmento.

  <------- 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  |
+================================-=============================================+

Dall'indirizzo più alto al più basso.

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
---------------------------------------------------------

Dettagli della registrazione dell'offerta

ID prodotto. A questo campo può essere applicato un valore ID prodotto univoco per questa immagine CFU.

UINT16 productID;  

Il traguardo del firmware è rappresentato dal contenuto dell'offerta. I traguardi possono essere versioni diverse della build HW, ad esempio, build EV1, build EV2 e così via. La definizione e l'assegnazione di valore cardine vengono lasciati allo sviluppatore.

UINT8 milestone : 3;

Se il firmware è destinato a una banca specifica, il campo a 2 bit supporta quattro banche. L'uso di un registro bancario è incluso nel formato dell'offerta perché esistono istanze in cui i dispositivi di destinazione usano aree del firmware bancario.

In tal caso, se l'offerta era destinata ad aggiornare una banca dati in uso, il firmware che implementa il CFU sulla destinazione può rifiutare l'offerta. In caso contrario, il firmware sul dispositivo che implementa CFU può eseguire altre azioni secondo necessità.

Se la gestione delle immagini del firmware non è inclusa nella progettazione del firmware dell'utente finale, è ragionevole ignorare questo campo (può essere impostato su qualsiasi valore conveniente, ma il valore nel campo di gestione è facoltativo e dipende da come il firmware sul dispositivo di destinazione implementa il CFU).

UINT8 bank : 2;

La versione del protocollo CFU usata è in 4 bit.

UINT8 protocolRevision : 4;

La maschera di bit corrispondente a tutti gli HW univoci su cui può funzionare questa immagine del firmware. Ad esempio, l'offerta può indicare che può essere eseguita su verX di HW, ma non su verY di HW. La definizione di bit e l'assegnazione di valori vengono lasciati allo sviluppatore.

UINT32 hwVariantMask;

Versione del firmware in offerta.

UINT32 version;

Token di byte per identificare il software specifico dell'utente che effettua l'offerta. Questo è progettato per distinguere i driver e gli strumenti che potrebbero entrambi tentare di aggiornare lo stesso firmware in esecuzione. Ad esempio, un driver di aggiornamento CFU può essere assegnato al token 0xA, mentre uno strumento di aggiornamento di sviluppo può essere assegnato al token 0xB. Ora il firmware in esecuzione può scegliere in modo selettivo di accettare o ignorare i comandi in base al processo che sta tentando di aggiornarlo.

UINT8 token;

Componente nel dispositivo per applicare l'aggiornamento del firmware.

UINT8 componentId;

fornire flag di interpretazione: se si vuole che il firmware in situ ignori la mancata corrispondenza della versione (versione precedente su quella più recente), impostare il bit per forzare Ignora Versione.

UINT8 forceIgnoreVersion: 1;

La reimpostazione immediata forzata viene asserta con un bit. Se tale bit viene asserito, il software host prevede che il firmware in situ causi che il dispositivo esegua una reimpostazione. Le azioni di reimpostazione sono specifiche della piattaforma. Il firmware del dispositivo può scegliere di intraprendere un'azione che comporti lo scambio dei banchi di memoria per rendere il firmware aggiornato di recente il firmware attivo in situ. O no. È lasciato all'implementazione del firmware. L'aspettativa di solito è che se la reimpostazione immediata forzata è asserita, il dispositivo farà tutto il necessario affinché il firmware renda aggiornato il nuovo bank e diventi il firmware attivo in esecuzione sul dispositivo di destinazione.

UINT8 forceImmediateReset : 1;

Nel caso in cui la parte di contenuto della coppia offerta-contenuto coinvolga diverse parti di contenuto.

UINT8 segmentNumber;

Elaborazione delle offerte

L'API ProcessCFWUOffer accetta due argomenti:

void ProcessCFWUOffer(FWUPDATE_OFFER_COMMAND* pCommand,
                     FWUPDATE_OFFER_RESPONSE* pResponse)

In questo caso d'uso, si supponga che il software utente invii i byte di dati al firmware in esecuzione, quindi il primo messaggio è il messaggio dell'offerta.

Il messaggio dell'offerta è un messaggio di 16 byte descritto in precedenza (la struttura FWUPDATE_OFFER_COMMAND).

Il messaggio dell'offerta è costituito dai dati usati dal firmware in esecuzione per gestire l'offerta.

Durante la disposizione dell'offerta, il firmware in esecuzione invia una notifica al mittente popolando i campi nella struttura FWUPDATE_OFFER_RESPONSE.

Interpretazione dell'offerta

Il firmware in esecuzione deve tenere traccia del suo stato nel processo CFU. Potrebbe essere pronto/in attesa di accettare un'offerta, nel mezzo di una transazione CFU o in attesa di cambiare banche tra firmware attivo/inattivo.

Se il firmware in esecuzione si trova nel mezzo di una transazione CFU, non accettare/elaborare l'offerta e notificare l'host di conseguenza.

   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;
   }

Il campo ID componente dell'offerta può essere usato per segnalare al firmware in esecuzione che viene richiesta un'azione speciale dal firmware in esecuzione. Nel codice CFU di esempio, un comando di offerta speciale viene usato dall'host per recuperare lo stato del motore CFU, indipendentemente dal fatto che il software in esecuzione sia in grado di accettare offerte CFU.

   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;
       }
   }

Infine, viene effettuata una verifica se c'è un swap bancario in sospeso. Il cambio di banco si riferisce al firmware che mantiene persistenti le informazioni sul fatto che sia ancora in corso il passaggio dall'applicazione in esecuzione alla nuova immagine scaricata.

Come e dove viene eseguito il cambio di banca è un'attività specifica di implementazione per il firmware incorporato. Il protocollo e il processo CFU consentono lo scambio di informazioni tra l'applicazione utente remota che esegue il CFU e il firmware in situ in esecuzione.

   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;
   }

Infine, se lo stato del firmware in esecuzione non risulta occupato e componentId non è un comando speciale e non c'è alcun scambio di memoria in sospeso, allora possiamo elaborare questa offerta.

L'elaborazione di un'offerta comporta, ma non solo, i quattro passaggi descritti di seguito:

Passaggio 1 - Verifica banca

Controllare la banca dell'applicazione in esecuzione alla banca nell'offerta. Sono uguali o diversi?

Se è lo stesso, rifiutare la proposta (non vogliamo sovrascrivere l'immagine in esecuzione/attiva).

In caso contrario, continua

Passaggio 2 - Controllare hwVariantMask

Il firmware in esecuzione controlla hwVariantMask nella proposta rispetto all'hardware su cui è in esecuzione. Ciò consente al firmware incorporato di rifiutare un'offerta se l'offerta non è valida per la destinazione. (ad esempio, se il firmware in esecuzione si trova in una build HW precedente e il nuovo firmware offerto è destinato a una build HW più recente, allora il firmware in esecuzione deve rifiutare questa offerta)

Se non è valido, rifiutare l'offerta.

Altrimenti continuare.

Passaggio 3 - Controllare la versione del firmware

Controllare se la versione del contenuto del firmware offerto ha una versione precedente o successiva rispetto al firmware dell'applicazione corrente.

Spetta all'implementazione degli utenti decidere come verificare quale firmware è superiore a un altro e se consentire l'uso del campo "forceIgnoreVersion" nell'offerta. Lo sviluppo tipico del firmware consente l'uso del campo "forceIgnoreVersion" durante lo sviluppo del prodotto e nelle versioni di debug del firmware, ma nel firmware di prodotto/rilascio è disabilitato, impedendo l'aggiornamento di un firmware precedente su un firmware più recente.

Se il controllo non è riuscito, rifiutare l'offerta.

Altrimenti, continua.

Passaggio 4 - Accettare l'offerta

L'offerta è buona. Accettare l'offerta con una risposta personalizzata per il modo in cui i messaggi e lo stato vengono restituiti dal firmware all'applicazione utente remota. La cosiddetta "risposta" è costituita da dati (una struttura di dati impacchettata come illustrato nei file di intestazione dimostrativi) e questi dati sono scritti nell'applicazione utente utilizzando i metodi appropriati per il dispositivo.

Elaborare il contenuto

L'elaborazione del contenuto è in genere un processo a più passaggi. I passaggi multipli fanno riferimento alla funzionalità del firmware per accettare l'immagine del firmware in parti, note anche come "blocchi" di dati. Non è sempre possibile inviare l'intera immagine contemporaneamente al firmware incorporato, quindi è realistico aspettarsi l'implementazione del protocollo CFU ed elaborare l'accettazione del contenuto in piccole parti.

Questa discussione si basa sull'assunzione nel descrivere il processo relativo al contenuto dei CFU.

La macchina a stati dell'elaborazione del contenuto prevede tre stati.

  1. Stato dell'elaborazione del primo blocco.

  2. Stato dell'elaborazione dell'ultimo blocco.

  3. Stato dell'elaborazione di qualsiasi blocco tra il primo e l'ultimo.

Struttura del comando content

Analogamente all'offerta, il contenuto ha una struttura con campi usati dagli algoritmi CFU nella dimostrazione.

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

La struttura del comando contenuto è più semplice della struttura dell'offerta. Il contenuto viene definito come sequenza di byte da scrivere in memoria. Il preambolo del contenuto è costituito dai campi di questa struttura:

  1. UINT8 flags Indica se il contenuto "blocco" è il primo, l'ultimo o un altro.

  2. UINT8 length Contrassegna la lunghezza del pData campo. Nel codice dimostrativo per CFU, il limite per le dimensioni di pData è di 255 byte. Altre implementazioni possono variare le dimensioni massime del "blocco".

  3. UINT16 sequenceNumber Contrassegna il contatore di indice del blocco che viene sottoposto come contenuto.

  4. UINT32 address Offset dell'indirizzo del blocco. Nella dimostrazione di CFU di questa versione, l'implementazione include informazioni predefinite sull'indirizzo fisico di ogni area dell'app. Ad esempio, un'implementazione di firmware a due banchi può prevedere che App1 inizi all'indirizzo 0x9000 e App2 inizi all'indirizzo 0xA0000. Pertanto, a seconda del modo in cui l'immagine del firmware è stata preparata (S-Records) l'indirizzo in SREC può essere l'indirizzo fisico o un offset. In ogni caso, deve esserci una comprensione condivisa tra la preparazione del contenuto e le routine specifiche di implementazione dell'elaborazione del contenuto CFU per determinare l'indirizzo fisico effettivo di dove scrivere il blocco in memoria. Spetta allo sviluppatore del firmware adottare le procedure consigliate ed eseguire controlli per gli intervalli di indirizzi validi per ogni blog di contenuto. Ad esempio, il codice CFU dimostra un controllo effettuato se forse App1 (destinato a 0x9000) ha indirizzi che si sovrappongono ad App2 e così via.

  5. UINT8 pData[MAX_UINT8] - Si tratta dei byte non elaborati del blocco di immagini del firmware. Prestare attenzione all'applicazione utente per inserire solo i byte length nel flusso di byte completo del blocco di contenuto.

Nella struttura del contenuto non sono utilizzati campi di bit in base alla dimostrazione CFU del codice fornito.

Primo blocco

Il primo blocco avvia il download del contenuto del firmware. Il firmware in esecuzione tenta di scrivere il blocco in memoria non volatile. Naturalmente il contenuto "blocco" contiene informazioni sulla posizione in cui deve essere scritto il blocco in memoria, la quantità di dati da scrivere e altri campi.

Ogni dispositivo di destinazione componentID è diverso e sono disponibili più metodi per rendere persistenti i dati in memoria. Ad esempio, un componentId potrebbe richiedere la scrittura in flash interno, un altro componentId può scrivere in un flash SPI esterno o un altro può utilizzare il protocollo I2C di un altro IC per aggiornarne l'immagine. La dimostrazione inclusa in questo documento evidenzia l'uso di una funzione denominata ICompFwUpdateBspWrite che ogni firmware univoco deve implementare con la conoscenza delle funzioni di I/O di memoria non volatili sottostanti della destinazione per cui è stato progettato.

Qualsiasi altro blocco tranne il primo o l'ultimo

Il processo di accettazione di nuovi blocchi continua quando l'applicazione utente recapita un altro blocco, di nuovo con metadati nel messaggio per l'indirizzo di dove deve essere scritto il blocco, il numero di byte contenuti e altri campi.

Il firmware in situ tratterebbe questo proprio come un primo scenario di blocco.

Tuttavia, si noti che in qualsiasi momento il sistema non riesce a acquisire e rendere persistente il blocco in memoria, spetta al firmware in situ rispondere con un codice di errore.

Ultimo blocco

L'ultimo blocco presenta una sfida solo se il firmware in situ deve eseguire attività per convalidare l'immagine appena scritta nella memoria.

In primo luogo, l'ultimo blocco viene scritto in memoria.

È quindi necessario eseguire almeno un controllo CRC tra i dati già scritti in memoria (dal primo all'ultimo blocco) rispetto al campo CRC nell'ultimo blocco. È lasciato a ogni firmware di implementazione sapere come acquisire il CRC per l'immagine scaricata.

Tenere presente che l'esecuzione del controllo CRC richiede tempo. A differenza del normale flusso di esecuzione del CFU per l'offerta e l'invio di blocchi. L'ultimo invio di blocco, se include un controllo CRC, avrà un certo ritardo solo per il fatto che il controllo CRC sta potenzialmente esaminando un'ampia area di memoria. A seconda del dispositivo di destinazione e di altri fattori, questo potrebbe non essere un problema.

Importante

Il controllo CRC dell'immagine in ingresso è facoltativo e può essere disabilitato tramite commento. Tuttavia, è consigliabile seguire le procedure migliori per adottare questo controllo. È consigliabile che a questo punto del processo CFU vengano eseguite altre azioni per garantire l'integrità dell'immagine scaricata. Alcune di queste azioni possono includere la verifica di una parte "firmata" dell'immagine e/o controllare le catene di certificati di attendibilità o altri approcci di procedure consigliate per garantire un'immagine del firmware sicura. Questi vengono lasciati allo sviluppatore del firmware.

Eseguire la pulizia dopo l'ultimo blocco

Dopo aver scritto l'ultimo blocco e aver completato il controllo CRC, il firmware potrebbe rispondere con un errore se una parte della convalida non è riuscita.

In caso contrario, si prevede che il processo CFU nel firmware risponda con uno stato di esito positivo.

Reimpostazione forzata selezionata

Il flag di reimpostazione forzata nell'offerta viene usato per determinare se l'MCU della destinazione deve subire una reimpostazione (reimpostazione definita dall'utente).

In genere, quando viene forzata una reimpostazione, la finalità consiste nell'eseguire una reimpostazione dell'MCU per fare in modo che la banca dell'app cambi. L'aggiornamento delle variabili persistenti per indicare l'immagine del firmware da avviare al riavvio è lasciato allo sviluppatore del firmware.