Compartir vía


Guía de implementación de firmware de actualización de firmware de componentes (CFU)

La actualización de firmware del componente (CFU) es un protocolo y un proceso para enviar nuevas imágenes de firmware que se van a instalar en el dispositivo de destino.

Nota

CFU está disponible en Windows 10, versión 2004 (Windows 10 actualización de mayo de 2020) y versiones posteriores.

Los envíos de CFU al firmware residente son pares de archivos, un archivo es la parte de la oferta, el otro archivo es la parte de contenido. Cada envío de CFU (cada oferta y par de contenido) debe crearse fuera de línea antes de enviar el envío al firmware que implementa el proceso de CFU.

En el código fuente de firmware de ejemplo del repositorio de CFU en GitHub, el código común independiente de implementación general para CFU se incluye en ComponentFwUpdate.c. Todos los demás archivos son archivos auxiliares que se pueden actualizar o modificar a la implementación única del desarrollador.

Contenido

Las partes de oferta y contenido

La oferta y el contenido componen un par de archivos en el esquema CFU.

La parte de la oferta es simplemente un archivo largo de 16 bytes que se asigna a la estructura de FWUPDATE_OFFER_COMMAND que se describe a continuación.

La parte de contenido, el firmware real que se va a actualizar está en el formato dictado por el desarrollador del usuario final. El código de ejemplo de CFU proporcionado usa archivos SREC para el contenido del firmware.

La oferta es una secuencia de 16 bytes. Esta estructura de oferta se coloca en el archivo de oferta. Básicamente, es datos binarios, no texto, porque la oferta contiene campos de bits de significado específico.

La oferta representada en el archivo se asigna a esta estructura de 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 baja dirección a dirección alta, el primer byte de la oferta es un 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  |
+================================-=============================================+

Desde la dirección alta hasta la dirección baja:

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

Detalles del registro de la oferta

Id. de producto. Se puede aplicar un valor de identificador de producto único para esta imagen de CFU a este campo.

UINT16 productID;  

Hito del firmware que representa el contenido de la oferta. Los hitos podrían ser diferentes versiones de la compilación de HW, por ejemplo, compilación ev1, compilación EV2, etc. La definición de hitos y la asignación de valores se dejan al desarrollador.

UINT8 milestone : 3;

Si el firmware está pensado para un banco específico, el campo de 2 bits admite cuatro bancos. El uso de un registro bancario se incluye en el formato de la oferta porque hay instancias en las que los dispositivos de destino usan regiones de firmware bancarias.

Si ese fuera el caso, y la oferta estaba pensada para actualizar un banco en uso, el firmware que implementa CFU en el destino puede rechazar la oferta. De lo contrario, el firmware en el destino que implementa CFU puede tomar otras medidas como se garantiza.

Si la banca de imágenes de firmware no está en el diseño del firmware del usuario final, es razonable omitir este campo (establecido en cualquier valor que sea conveniente, pero el valor del campo bancario es opcional y depende de la forma en que el firmware de destino implementa CFU).

UINT8 bank : 2;

La versión de protocolo del protocolo CFU utilizada se encuentra en 4 bits.

UINT8 protocolRevision : 4;

La máscara de bits correspondiente a todos los HW únicos en la que puede funcionar esta imagen de firmware. Por ejemplo, la oferta puede indicar que se puede ejecutar en verX de HW, pero no en verY de HW. La definición de bits y la asignación de valores se dejan al desarrollador.

UINT32 hwVariantMask;

Versión del firmware que se ofrece.

UINT32 version;

Token de bytes para identificar el software específico del usuario que realiza la oferta. Esto está pensado para diferenciar entre los controladores y las herramientas que pueden estar intentando actualizar el mismo firmware en ejecución. Por ejemplo, se puede asignar un controlador de actualización de CFU 0xA de tokens y se puede asignar una herramienta de actualizador de desarrollo 0xB. Ahora, el firmware en ejecución puede elegir de forma selectiva aceptar o omitir comandos en función del proceso que intenta actualizarlo.

UINT8 token;

Componente del dispositivo que se va a aplicar la actualización de firmware.

UINT8 componentId;

marcas de interpretación de la oferta: si queremos que el firmware in situ omita la coincidencia de versiones (anterior en la parte superior de la más reciente), establezca el bit para forzar Omitir versión.

UINT8 forceIgnoreVersion: 1;

Forzar el restablecimiento inmediato se afirma con un bit. Si se afirma ese bit, el software host espera que el firmware in situ haga que el dispositivo realice un restablecimiento. Las acciones del restablecimiento son específicas de la plataforma. El firmware del dispositivo puede optar por tomar medidas que intercambian bancos para actualizar el firmware activo en el entorno local. O no. Se deja hasta la implementación del firmware. La expectativa suele ser que si se afirma el restablecimiento inmediato forzado, el dispositivo hará lo que sea necesario para hacer que el firmware haga que el nuevo banco actualizado se convierta en el firmware activo que se ejecuta en el dispositivo de destino.

UINT8 forceImmediateReset : 1;

En caso de que la parte de contenido de la oferta y el par de contenido implique varias partes del contenido.

UINT8 segmentNumber;

Procesamiento de ofertas

La API ProcessCFWUOffer acepta dos argumentos:

void ProcessCFWUOffer(FWUPDATE_OFFER_COMMAND* pCommand,
                     FWUPDATE_OFFER_RESPONSE* pResponse)

En este caso de uso, supongamos que el software de usuario envía bytes de datos al firmware en ejecución y, a continuación, el primer mensaje es el mensaje de la oferta.

El mensaje de la oferta es un mensaje de 16 bytes descrito anteriormente (la estructura FWUPDATE_OFFER_COMMAND).

Ese mensaje de oferta es los datos que usa el firmware en ejecución para eliminar la oferta.

Durante la eliminación de la oferta, el firmware en ejecución notifica al remitente rellenando los campos de la FWUPDATE_OFFER_RESPONSE estructura.

Interpretación de la oferta

El firmware en ejecución debe realizar un seguimiento de su estado en el proceso de CFU. Puede estar listo/esperando para aceptar una oferta, en medio de una transacción de CFU o esperando a intercambiar bancos entre firmware activo o inactivo.

Si el firmware en ejecución está en medio de una transacción de CFU, no acepte o procese esta oferta y notifique al host en consecuencia.

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

El campo id. de componente de la oferta se puede usar para indicar el firmware en ejecución que se solicita una acción especial desde el firmware en ejecución. En el código CFU de ejemplo, el host usa un comando de oferta especial para recuperar el estado del motor de CFU, si el software en ejecución es capaz y está listo para aceptar 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 último, se realiza una comprobación si hay un intercambio bancario pendiente. El intercambio bancario hace referencia al firmware que conserva la información sobre si todavía está en proceso de cambiar de la aplicación activa en ejecución a la imagen recién descargada.

Cómo y dónde se realiza la conmutación bancaria es una tarea específica de implementación para el firmware insertado. El proceso y el protocolo CFU permiten intercambiar información entre la aplicación de usuario remoto que lleva a cabo la CFU y el firmware in situ que se está ejecutando.

   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 último, si el estado del firmware en ejecución no está ocupado y el componentId no es un comando especial y no hay ningún intercambio bancario pendiente: ENTONCES podemos procesar esta oferta.

El procesamiento de una oferta implica, pero no está limitado a, los cuatro pasos que se describen a continuación:

Paso 1: Comprobar banco

Compruebe el banco de la aplicación en ejecución al banco de la oferta. ¿Son iguales o diferentes?

Si es lo mismo, rechace la oferta (no queremos sobrescribir la imagen en ejecución o activa).

En caso contrario, continúe.

Paso 2: Comprobar hwVariantMask

El firmware en ejecución comprueba en hwVariantMask la oferta el HW en el que se ejecuta. Esto permite que el firmware insertado rechace una oferta si la oferta no es válida para el destino. (por ejemplo, si el firmware en ejecución está en una compilación anterior de HW y el nuevo firmware ofrecido está pensado para una compilación más reciente, HW; el firmware en ejecución debe rechazar esta oferta).

Si no es válido, rechace la oferta.

En caso contrario, continúe.

Paso 3: Comprobación de la versión del firmware

Compruebe si la versión del contenido de firmware que se ofrece tiene una versión anterior o más reciente que el firmware de la aplicación actual.

Se deja que la implementación de los usuarios decida cómo comprobar qué firmware es mayor que otro y si se permite usar el campo "forceIgnoreVersion" de la oferta. El desarrollo de firmware típico permitiría usar el campo "forceIgnoreVersion" durante el desarrollo del producto y en las versiones de depuración del firmware, pero no permitidos (no permitir que el firmware anterior se actualice sobre el firmware nuevo) en el firmware del producto o versión.

Si se ha producido un error en esta comprobación, rechace la oferta.

De lo contrario, continúe.

Paso 4: Aceptar oferta

La oferta es buena. Acepte la oferta con una respuesta adaptada a la forma en que el firmware devuelve los mensajes y el estado a la aplicación de usuario remota. La llamada "respuesta" es datos (una estructura de datos empaquetada como se muestra en los archivos de encabezado de demostración) y estos datos se escriben en la aplicación de usuario por los medios adecuados para el dispositivo.

Procesar el contenido

El procesamiento del contenido suele ser un proceso de varios pasos. Los pasos múltiples hacen referencia a la funcionalidad del firmware para aceptar la imagen de firmware en partes, también conocidas como "bloques" de datos. No siempre es factible enviar toda la imagen a la vez al firmware incrustado, por lo que es realista esperar la implementación del protocolo CFU y el proceso para aceptar contenido en pequeños fragmentos.

En esta discusión se usa la suposición al describir el proceso del contenido de la CFU.

La máquina de estado del procesamiento de contenido implica tres estados.

  1. Estado del procesamiento del primer bloque.

  2. Estado del procesamiento del último bloque.

  3. Estado del procesamiento de cualquier bloque entre el primero y el último.

Estructura del comando de contenido

Al igual que la oferta, el contenido tiene una estructura con campos que usan los algoritmos de CFU en la demostración.

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

La estructura del comando content es más sencilla que la estructura de la oferta. El contenido se define como una secuencia de bytes que se escribirán en la memoria. El preámbulo del contenido es los campos de esta estructura:

  1. UINT8 flags Indica si el contenido "bloque" es el primero, último u otro.

  2. UINT8 length Marca la longitud del pData campo. En el código de demostración de CFU, el límite del tamaño de es de pData 255 bytes. Otras implementaciones pueden variar el tamaño máximo del "bloque".

  3. UINT16 sequenceNumber Marca el contador de índice del que se envía el bloque como contenido.

  4. UINT32 address Desplazamiento de dirección del bloque. En la demostración de CFU de esta versión, la implementación tiene información predefinida sobre la dirección física de cada región de la aplicación. Por ejemplo, una implementación de firmware de dos bancos puede hacer que App1 comience en la dirección 0x9000 y App2 comience en la dirección 0xA0000. Por lo tanto, dependiendo de cómo se preparó la imagen de firmware (S-Records), la dirección en el SREC puede ser la dirección física o un desplazamiento. En cualquier caso, debe haber una comprensión compartida entre la preparación del contenido y las rutinas específicas de implementación del procesamiento de contenido de CFU para determinar la dirección física verdadera de dónde escribir el bloque en memoria. Se deja que el desarrollador de firmware adopte los procedimientos recomendados y realice comprobaciones de intervalos de direcciones válidos para cada blog de contenido. Por ejemplo, el código CFU muestra una comprobación realizada si tal vez App1 (pensada para 0x9000) tiene direcciones que se superponen en App2, etc.

  5. UINT8 pData[MAX_UINT8] : se trata de los bytes sin procesar del bloque de imagen de firmware. Se tiene cuidado en la aplicación de usuario para colocar length solo bytes en la secuencia de bytes completa del bloque de contenido.

No se usan campos de bits en la estructura de contenido según la demostración de CFU del código proporcionado.

Primer bloque

El primer bloque inicia la descarga del contenido del firmware. El firmware que ejecuta intenta escribir el bloque en memoria no volátil. Por supuesto, el contenido "bloque" contiene información sobre dónde se debe escribir el bloque en memoria, la cantidad de datos que se van a escribir y otros campos.

Cada dispositivo de destino componentID es diferente y hay varios métodos para conservar los datos en la memoria. Por ejemplo, un componentId podría requerir la escritura en flash interno, otro componentId puede escribir en un flash SPI externo u otro puede usar el protocolo I2C de otro IC para actualizar su imagen. La demostración incluida con este documento resalta el uso de una función denominada ICompFwUpdateBspWrite que cada firmware único debe implementar con conocimiento de las funciones de E/S de memoria no volátil subyacentes del destino para el que se diseñó.

Cualquier otro bloque excepto el primero o el último

El proceso de aceptar nuevos bloques continúa cuando la aplicación de usuario entrega otro bloque, de nuevo con metadatos en el mensaje para la dirección de dónde se debe escribir el bloque, cuántos bytes están contenidos y otros campos.

El firmware in situ trataría esto como un primer escenario de bloque.

Sin embargo, debe tenerse en cuenta que, en cualquier momento, el sistema no puede capturar y conservar el bloque en la memoria, es el firmware in situ para responder con un código de error.

Último bloque

El último bloque presenta un desafío solo si el firmware in situ necesita realizar tareas para validar la imagen que se acaba de escribir en la memoria.

En primer lugar, el último bloque se escribe en la memoria.

A continuación, como mínimo, se debe realizar una comprobación de CRC entre los datos ya escritos en la memoria (de los primeros a los últimos bloques) en comparación con el campo CRC en el último bloque. Se deja que cada firmware de implementación sepa cómo adquirir el CRC para la imagen descargada.

Tenga en cuenta que la ejecución de la comprobación del CRC lleva tiempo. A diferencia del flujo normal de la ejecución de la CFU para el envío de ofertas y bloqueos. El último envío de bloque, si incluye una comprobación de CRC, tendrá un cierto retraso en el hecho de que la comprobación del CRC está examinando potencialmente una gran región de memoria. Dependiendo del dispositivo de destino y otros factores, esto puede no ser un problema.

Importante

La comprobación CRC de la imagen entrante es opcional y puede ser comentada. Sin embargo, los procedimientos recomendados deben aplicarse al menos para adoptar esta comprobación. Se recomienda encarecidamente que en este momento del proceso de la CFU se realicen otras acciones para garantizar la integridad de la imagen descargada. Algunas de estas acciones podrían incluir comprobar una parte "firmada" de la imagen o comprobar las cadenas de certificados de confianza u otros métodos recomendados para garantizar una imagen de firmware segura. Estos se dejan al desarrollador de firmware.

Limpieza después del último bloque

Ahora que se escribe el último bloque y se completa la comprobación crc, el firmware puede responder con un error si alguna parte de la validación no se pudo realizar.

De lo contrario, la expectativa es que el proceso de CFU en el firmware responda con un estado correcto.

Restablecimiento forzado activado

La marca de restablecimiento forzado de la oferta se usa para determinar si la MCU del destino se somete a un restablecimiento (restablecimiento definido por el usuario).

Normalmente, cuando se fuerza un restablecimiento, la intención es hacer que la MCU realice un restablecimiento para hacer que el banco de aplicaciones cambie. La actualización de variables persistentes para indicar en qué imagen de firmware arrancar al restablecer se deja al desarrollador de firmware.