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.

Nota

CFU è disponibile in Windows 10 versione 2004 (Windows 10 aggiornamento di maggio 2020) 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 contenuti) deve essere creato offline prima che l'invio venga inviato al firmware che implementa il processo CFU.

Nel codice sorgente del firmware di esempio nel repository CFU in GitHub, il codice comune di implementazione generale 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

Parti dell'offerta e del contenuto

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

La parte dell'offerta è semplicemente un file lungo 16 byte che esegue il mapping 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 esegue il mapping 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  |
+================================-=============================================+

Da indirizzo elevato a basso indirizzo:

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;  

L'attività cardine del firmware rappresentata dal contenuto dell'offerta. Le attività cardine possono essere versioni diverse della build HW, ad esempio build EV1, build EV2 e così via. La definizione delle attività cardine e l'assegnazione di valore vengono lasciate 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, e l'offerta era destinata ad aggiornare una banca in uso, il firmware che implementa CFU sul target può rifiutare l'offerta. In caso contrario, il firmware nella destinazione che implementa CFU può eseguire altre azioni come garantito.

Se il sistema bancario delle immagini del firmware non è nella progettazione del firmware dell'utente finale, è ragionevole ignorare questo campo (impostato su qualsiasi valore conveniente, ma il valore nel campo della banca è facoltativo e dipende dal modo in cui il firmware di destinazione implementa 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ò operare 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 offerta.

UINT32 version;

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

UINT8 token;

Componente nel dispositivo per applicare l'aggiornamento del firmware.

UINT8 componentId;

offrire flag di interpretazione: se si vuole che il firmware in situ ignori la mancata corrispondenza della versione (precedente sopra il 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 un ripristino. Le azioni di reimpostazione sono specifiche della piattaforma. Il firmware del dispositivo può scegliere di intraprendere azioni che scambiano le banche per rendere il firmware aggiornato appena aggiornato il firmware attivo in situ firmware. O no. Rimane fino all'implementazione del firmware. L'aspettativa è in genere che, se la reimpostazione immediata forzata viene affermata, che il dispositivo eseguirà qualsiasi operazione necessaria per fare in modo che il firmware venga aggiornato dalla nuova banca diventi il firmware attivo in esecuzione nel dispositivo di destinazione.

UINT8 forceImmediateReset : 1;

Nel caso in cui la parte di contenuto dell'offerta e della coppia di contenuto coinvolge più parti del 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 a 16 byte descritto in precedenza (la struttura FWUPDATE_OFFER_COMMAND).

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

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

Interpretazione dell'offerta

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

Se il firmware in esecuzione si trova al centro di una transazione CFU, non accettare/elaborare l'offerta e inviare una notifica all'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 le 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 effettuato un assegno se c'è uno scambio bancario in sospeso. Lo scambio bancario fa riferimento al firmware che rende persistenti le informazioni relative al fatto che sia ancora in corso il passaggio dall'applicazione in esecuzione e attiva all'immagine appena 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 conduce 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 è occupato e componentId non è un comando speciale e non c'è alcun scambio bancario in sospeso, quindi è possibile elaborare questa offerta.

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

Passaggio 1 - Assegno bancario

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

Se lo stesso, rifiutare l'offerta (non si vuole sovrascrivere l'immagine in esecuzione/attiva).

Altrimenti continuare.

Passaggio 2 - Controllare hwVariantMask

Il firmware in esecuzione controlla l'oggetto hwVariantMask nell'offerta rispetto all'HW in 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 più recente, HW, il firmware in esecuzione dovrebbe 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 controllare quale firmware è maggiore di un altro e se consentire l'uso del campo "forceIgnoreVersion" nell'offerta. Lo sviluppo tipico del firmware consentirebbe l'uso del campo "forceIgnoreVersion" durante lo sviluppo del prodotto e nelle versioni di debug del firmware ma non consentito (non consentendo l'aggiornamento del firmware precedente sopra il nuovo firmware) nel firmware di prodotto/rilascio.

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

Altrimenti continuare.

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. Il cosiddetto "risposta" è costituito da dati (una struttura di dati compressa, come illustrato nei file di intestazione dimostrativi) e questi dati vengono scritti nell'applicazione dall'utente in base ai mezzi 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 usa il presupposto quando si descrive il processo del contenuto 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 compreso tra primo e 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 content è 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 "block" è il primo, l'ultimo o l'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 quale il blocco viene inviato 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 del firmware di due banche può avere App1 iniziare all'indirizzo 0x9000 e App2 inizia 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, è necessario avere 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 ad esempio App1 (destinato a 0x9000) contiene 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 length solo i byte nel flusso di byte completo del blocco di contenuto.

Non sono presenti campi di bit usati nella struttura del contenuto 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 scrivere 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 conoscenza delle funzioni di I/O di memoria non volatili sottostanti della destinazione per cui è stato progettato.

Qualsiasi altro blocco tranne primo o 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 tratta questo esattamente 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. Viene 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. Diversamente dal normale flusso dell'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 impostato come commento. Tuttavia, le procedure consigliate devono essere applicate almeno 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 del controllo delle 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.

Pulire dopo l'ultimo blocco

Ora che l'ultimo blocco viene scritto e il controllo CRC è completo, il firmware può rispondere con un errore se una parte della convalida non è riuscita.

In caso contrario, l'aspettativa è che il processo CFU nel firmware risponderà 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 essere reimpostata (reimpostazione definita dall'utente).

In genere, quando viene forzata una reimpostazione, la finalità consiste nel fare in modo che l'MCU esecuzione venga reimpostata per fare in modo che la banca dell'app cambi. Aggiornamento di variabili persistenti per indicare l'immagine del firmware in cui eseguire l'avvio al ripristino viene lasciata allo sviluppatore del firmware.