Partilhar via


Guia de implementação de firmware da CFU (Atualização de Firmware de Componente)

A CFU (Atualização de Firmware de Componente) é um protocolo e um processo para enviar novas imagens de firmware a serem instaladas no dispositivo de destino.

Observação

O CFU está disponível em Windows 10, versão 2004 (Windows 10 Atualização de maio de 2020) e versões posteriores.

Os envios de CFU para o firmware residente são pares de arquivos, um arquivo é a parte da oferta, o outro arquivo é a parte de conteúdo. Cada envio de CFU (cada oferta e par de conteúdo) é necessário para ser criado off-line antes que o envio seja enviado para o firmware que implementa o processo cfu.

No código-fonte firmware de exemplo no repositório CFU no GitHub, o código comum independente de implementação geral para CFU está contido em ComponentFwUpdate.c. Todos os outros arquivos são arquivos auxiliares que podem ser atualizados ou modificados para a implementação exclusiva do desenvolvedor.

Sumário

As partes de oferta e conteúdo

A oferta e o conteúdo compõem um par de arquivos no esquema CFU.

A parte da oferta é simplesmente um arquivo longo de 16 bytes que mapeia para a estrutura de FWUPDATE_OFFER_COMMAND descrita abaixo.

A parte de conteúdo, o firmware real a ser atualizado, está no formato ditado pelo desenvolvedor do usuário final. O código de exemplo cfu fornecido usa arquivos SREC para conteúdo de firmware.

A oferta é uma sequência de 16 bytes. Essa estrutura de oferta é colocada no arquivo de oferta. São essencialmente dados binários, não texto, porque a oferta contém campos de bits de significado específico.

A oferta representada no arquivo é mapeada para esta estrutura 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;

De endereço baixo a endereço alto, o primeiro byte da oferta é um número de 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  |
+================================-=============================================+

Do endereço alto ao endereço baixo:

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

Detalhes do registro da oferta

A ID do produto. Um valor de ID de produto exclusivo para essa imagem cfu pode ser aplicado a esse campo.

UINT16 productID;  

O marco do firmware que o conteúdo da oferta representa. Os marcos podem ser versões diferentes do build HW, por exemplo, build EV1, build EV2 e assim por diante. A definição de marco e a atribuição de valor são deixadas para o desenvolvedor.

UINT8 milestone : 3;

Se o firmware for destinado a um banco específico , o campo de 2 bits oferecerá suporte a quatro bancos. O uso de um registro bancário está incluído no formato da oferta porque há instâncias em que os dispositivos de destino usam regiões de firmware bancárias.

Se esse fosse o caso, e a oferta foi destinada a atualizar um banco em uso, o firmware que implementa CFU no destino pode rejeitar a oferta. Caso contrário, o firmware no destino que implementa o CFU pode tomar outras medidas conforme garantido.

Se o banco de imagens de firmware NÃO estiver no design do firmware do usuário final, será razoável ignorar esse campo (definido como quaisquer valores convenientes, mas o valor no campo bancário é opcional e depende da maneira como o firmware de destino implementa o CFU).

UINT8 bank : 2;

A versão de protocolo do protocolo CFU usada está em 4 bits.

UINT8 protocolRevision : 4;

A máscara de bits correspondente a todos os HW exclusivos em que essa imagem de firmware pode operar. Por exemplo, a oferta pode significar que ela pode ser executada no verX do HW, mas não na verY do HW. A definição de bits e a atribuição de valor são deixadas para o desenvolvedor.

UINT32 hwVariantMask;

A versão do firmware que está sendo oferecido.

UINT32 version;

Um token de byte para identificar o software específico do usuário que está fazendo a oferta. Isso se destina a diferenciar entre drivers e ferramentas que podem estar tentando atualizar o mesmo firmware em execução. Por exemplo, um driver de atualização de CFU pode receber 0xA de token e uma ferramenta de atualizador de desenvolvimento pode ser atribuída 0xB. Agora, o firmware em execução pode optar seletivamente por aceitar ou ignorar comandos com base em qual processo está tentando atualizá-lo.

UINT8 token;

O componente no dispositivo para aplicar a atualização de firmware.

UINT8 componentId;

sinalizadores de interpretação de oferta: se quisermos que o firmware in situ ignore a incompatibilidade de versão (mais antigo na parte superior do mais recente), defina o bit para forçar Ignorar Versão.

UINT8 forceIgnoreVersion: 1;

Forçar a redefinição imediata é declarado com um bit. Se esse bit for declarado, o software host espera que o firmware in situ faça com que o dispositivo execute uma redefinição. As ações da redefinição são específicas da plataforma. O firmware do dispositivo pode optar por executar uma ação que troque os bancos para tornar o firmware recém-atualizado o firmware in situ ativo. Ou não. Ele é deixado para a implementação do firmware. A expectativa geralmente é que, se a força de redefinição imediata for afirmada, o dispositivo fará o que for necessário para fazer com que o novo banco atualizado se torne o firmware ativo em execução no dispositivo de destino.

UINT8 forceImmediateReset : 1;

Caso a parte de conteúdo da oferta e do par de conteúdo envolva várias partes do conteúdo.

UINT8 segmentNumber;

Ofertas de processamento

A API ProcessCFWUOffer aceita dois argumentos:

void ProcessCFWUOffer(FWUPDATE_OFFER_COMMAND* pCommand,
                     FWUPDATE_OFFER_RESPONSE* pResponse)

Nesse caso de uso, suponha que o software do usuário envie bytes de dados para o firmware em execução e, em seguida, a primeira mensagem é a mensagem de oferta.

A mensagem de oferta é uma mensagem de 16 bytes descrita acima (a estrutura FWUPDATE_OFFER_COMMAND).

Essa mensagem de oferta são os dados usados pelo firmware em execução para disposição da oferta.

Durante a disposição da oferta, o firmware em execução notifica o remetente preenchendo campos na FWUPDATE_OFFER_RESPONSE estrutura.

Interpretando a oferta

O firmware em execução deve acompanhar seu estado no processo de CFU. Pode estar pronto/aguardando para aceitar uma oferta, no meio de uma transação CFU, ou aguardando para trocar bancos entre firmware ativo/inativo.

Se o firmware em execução estiver no meio de uma transação CFU, não aceite/processe essa oferta e notifique o host adequadamente.

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

O campo ID do componente da oferta pode ser usado para sinalizar o firmware em execução de que uma ação especial é solicitada do firmware em execução. No código CFU de exemplo, um comando de oferta especial é usado pelo host para recuperar o status do mecanismo cfu - se o software em execução é capaz e pronto para aceitar ofertas de 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;
       }
   }

Por fim, um marcar será feito se houver uma troca de banco pendente. A troca bancária refere-se ao firmware que persiste as informações sobre se ele ainda está ou não em processo de mudança do aplicativo ativo em execução para a imagem de download recente.

Como e onde a troca bancária é executada é uma tarefa específica de implementação para o firmware inserido. O protocolo e o processo cfu permitem que as informações sejam trocadas entre o aplicativo de usuário remoto que conduz o CFU e o firmware in situ que está em execução.

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

Por fim, se o estado do firmware em execução não estiver ocupado e a componentId não for um comando especial e não houver nenhuma troca bancária pendente , então podemos processar essa oferta.

O processamento de uma oferta envolve, mas não se limita a, as quatro etapas descritas abaixo:

Etapa 1 – Verificar banco

Verifique o banco do aplicativo em execução no banco na oferta. Eles são iguais ou diferentes?

Se o mesmo for, rejeite a oferta (não queremos substituir a imagem em execução/ativa).

Caso contrário, continue.

Etapa 2 – Verificar hwVariantMask

O firmware em execução verifica o hwVariantMask na oferta em relação ao HW em que está sendo executado. Isso permite que o firmware inserido rejeite uma oferta se a oferta for inválida para o destino. (por exemplo, se o firmware em execução estiver em um build HW antigo e o novo firmware oferecido for destinado a um build HW mais recente, o firmware em execução deverá rejeitar essa oferta)

Se for inválido, rejeite a oferta.

Caso contrário, continue.

Etapa 3 – Verificar a versão do firmware

Verifique se a versão do conteúdo de firmware oferecido tem uma versão mais antiga ou mais recente do que o firmware do aplicativo atual.

Cabe à implementação dos usuários decidir como marcar qual firmware é maior que outro e se permitir que o campo 'forceIgnoreVersion' na oferta seja usado. O desenvolvimento de firmware típico permitiria que o campo 'forceIgnoreVersion' fosse usado durante o desenvolvimento de produtos e em versões de depuração do firmware, mas não permitido (não permitindo que firmware mais antigo seja atualizado sobre o novo firmware) no firmware de produto/lançamento.

Se esse marcar falhou, rejeite a oferta.

Caso contrário, continue.

Etapa 4 – Aceitar oferta

A oferta é boa. Aceite a oferta com uma resposta personalizada para a maneira como as mensagens e status são retornados pelo firmware para o aplicativo de usuário remoto. A chamada "resposta" são dados (uma estrutura de dados empacotada, conforme mostrado nos arquivos de cabeçalho de demonstração) e esses dados são gravados no aplicativo do usuário pelos meios apropriados para o dispositivo.

Processar o conteúdo

O processamento do conteúdo geralmente é um processo de várias etapas. As várias etapas referem-se à capacidade do firmware de aceitar a imagem de firmware em partes, também conhecidas como "blocos" de dados. Nem sempre é viável enviar a imagem inteira de uma só vez para o firmware inserido, portanto, é realista esperar que a implementação do protocolo CFU e o processo aceitem conteúdo em partes pequenas.

Essa discussão usa a suposição ao descrever o processo do conteúdo da CFU.

A máquina de estado do processamento de conteúdo envolve três estados.

  1. O estado do processamento do primeiro bloco.

  2. O estado do processamento do último bloco.

  3. O estado de processamento de qualquer bloco entre o primeiro e o último.

A estrutura do comando de conteúdo

Assim como a oferta, o conteúdo tem uma estrutura com campos que são usados pelos algoritmos cfu na demonstração.

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

A estrutura do comando de conteúdo é mais simples do que a estrutura de oferta. O conteúdo é definido como uma sequência de bytes a serem gravados na memória. O preâmbulo do conteúdo são os campos desta estrutura:

  1. UINT8 flags Indica se o conteúdo "bloquear" é o primeiro, o último ou outro.

  2. UINT8 length Marca o comprimento do pData campo. No código de demonstração para CFU, o limite no tamanho do pData é de 255 bytes. Outras implementações podem variar o tamanho máximo do "bloco".

  3. UINT16 sequenceNumber Marca o contador de índice de qual bloco está sendo enviado como conteúdo.

  4. UINT32 address O deslocamento de endereço do bloco. Na demonstração da CFU desta versão, a implementação tem informações predefinidas sobre o endereço físico de cada região do aplicativo. Por exemplo, uma implementação de firmware de dois bancos pode fazer com que o App1 comece no endereço 0x9000 e o App2 comece no endereço 0xA0000. Portanto, dependendo de como a imagem de firmware foi preparada (S-Records), o endereço no SREC pode ser o endereço físico ou um deslocamento. De qualquer forma, precisa haver uma compreensão compartilhada entre a preparação do conteúdo e as rotinas específicas de implementação do processamento de conteúdo cfu para determinar o endereço físico verdadeiro de onde gravar o bloco na memória. Cabe ao desenvolvedor de firmware adotar as práticas recomendadas e verificar se há intervalos de endereços válidos para cada blog de conteúdo. Por exemplo, o código CFU demonstra uma marcar feita se talvez App1 (destinado a 0x9000) tenha endereços que se sobrepõem ao App2 e assim por diante.

  5. UINT8 pData[MAX_UINT8] - Esses são os bytes brutos do bloco de imagem de firmware. O cuidado é tomado no aplicativo do usuário para colocar length apenas bytes no fluxo de bytes completo do bloco de conteúdo.

Não há campos de bits usados na estrutura de conteúdo de acordo com a demonstração de CFU do código fornecido.

O primeiro bloco

O primeiro bloco inicia o download do conteúdo do firmware. O firmware em execução tenta gravar o bloco na memória não volátil. É claro que o conteúdo "bloco" contém informações sobre onde na memória o bloco deve ser gravado, quantos dados gravar e outros campos.

Cada dispositivo de destino componentID é diferente e há vários métodos para persistir os dados na memória. Por exemplo, um componentId pode exigir gravação em flash interno, outro componentId pode gravar em um flash SPI externo ou outro pode utilizar outro protocolo I2C do IC para atualizar sua imagem. A demonstração incluída neste documento destaca o uso de uma função chamada ICompFwUpdateBspWrite que cada firmware exclusivo deve implementar com conhecimento das funções de E/S de memória não voláteis subjacentes do destino para o qual foi projetado.

Qualquer outro bloco, exceto primeiro ou último

O processo de aceitação de novos blocos continua quando o aplicativo-usuário entrega outro bloco, novamente com metadados na mensagem para o endereço de onde o bloco deve ser gravado, quantos bytes estão contidos e outros campos.

O firmware in situ trataria isso como um cenário de primeiro bloco.

No entanto, deve-se observar que a qualquer momento o sistema não consegue capturar e persistir o bloco na memória, cabe ao firmware in situ responder com um código de falha.

O último bloco

O último bloco só apresentará um desafio se o firmware in situ precisar realizar tarefas para validar a imagem que acabou de ser gravada na memória.

Primeiro, o último bloco é gravado na memória.

Em seguida, no mínimo, uma marcar crc deve ser feita entre os dados já gravados na memória (do primeiro ao último bloco) em comparação com o campo CRC no último bloco. Cabe a cada firmware de implementação saber como adquirir o CRC para a imagem baixada.

Tenha em mente que a execução do crc marcar leva tempo. Ao contrário do fluxo normal da execução da CFU para envio de oferta e de bloco. O último envio de bloco, se incluir uma marcar crc, terá um certo atraso envolvido apenas pelo fato de que o CRC marcar está potencialmente examinando uma grande região de memória. Dependendo do dispositivo de destino e de outros fatores, isso pode não ser uma preocupação.

Importante

A marcar CRC da imagem de entrada é opcional e pode ser comentada. No entanto, devem ser implementadas práticas recomendadas para, pelo menos, adotar esse marcar. É altamente recomendável que, neste ponto do processo cfu, outras ações sejam tomadas para garantir a integridade da imagem baixada. Algumas dessas ações podem incluir verificar uma parte "assinada" da imagem e/ou marcar cadeias de certificados de confiança ou outras abordagens de práticas recomendadas para garantir uma imagem de firmware segura. Eles são deixados para o desenvolvedor de firmware.

Limpar após o último bloco

Agora que o último bloco foi gravado e o crc marcar concluído, o firmware poderá responder com uma falha se alguma parte da validação falhar.

Caso contrário, a expectativa é que o processo de CFU no firmware responda com um status bem-sucedido.

Redefinição forçada verificada

O sinalizador de redefinição forçada na oferta é usado para determinar se o MCU do destino deve passar por uma redefinição (redefinição definida pelo usuário).

Normalmente, quando uma redefinição é forçada, a intenção é fazer com que o MCU faça uma redefinição para fazer com que o banco de aplicativos mude. A atualização de variáveis persistentes para indicar em qual imagem de firmware inicializar na redefinição é deixada para o desenvolvedor de firmware.