Guide d’implémentation du microprogramme CFU (Component Firmware Update)
La mise à jour du microprogramme de composants (CFU) est un protocole et un processus permettant de soumettre de nouvelles images de microprogramme à installer sur l’appareil cible.
Notes
CFU est disponible dans Windows 10, version 2004 (Windows 10 mise à jour de mai 2020) et versions ultérieures.
Les soumissions CFU au microprogramme résident sont des paires de fichiers, un fichier est la partie de l’offre, l’autre fichier est la partie contenu. Chaque soumission CFU (chaque paire d’offres et de contenu) doit être créée hors ligne avant que la soumission soit envoyée au microprogramme qui implémente le processus CFU.
Dans l’exemple de code source du microprogramme dans le référentiel CFU sur GitHub, le code commun indépendant de l’implémentation générale pour CFU est contenu dans ComponentFwUpdate.c
. Tous les autres fichiers sont des fichiers d’assistance qui peuvent être mis à jour ou modifiés en fonction de l’implémentation unique du développeur.
Contenu
- L’offre et les parties de contenu
L’offre et les parties de contenu
L’offre et le contenu constituent une paire de fichiers dans le schéma CFU.
La partie de l’offre est simplement un fichier de 16 octets qui correspond à la structure FWUPDATE_OFFER_COMMAND décrite ci-dessous.
La partie contenu, le microprogramme réel à mettre à jour, est au format dicté par le développeur de l’utilisateur final. L’exemple de code CFU fourni utilise des fichiers SREC pour le contenu du microprogramme.
L’offre est une séquence de 16 octets. Cette structure d’offre est placée dans le fichier de l’offre. Il s’agit essentiellement de données binaires, et non de texte, car l’offre contient des champs de bits de signification spécifique.
L’offre représentée dans le fichier est mappée à cette structure 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 l’adresse faible à l’adresse haute, le premier octet de l’offre est un numéro de segment.
<------- 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 |
+================================-=============================================+
De l’adresse haute à l’adresse basse :
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
---------------------------------------------------------
Détails du registre de l’offre
ID de produit. Une valeur d’ID de produit unique pour cette image CFU peut être appliquée à ce champ.
UINT16 productID;
Jalon du microprogramme représenté par le contenu de l’offre. Les jalons peuvent être des versions différentes de la build HW, par exemple, la build EV1, la build EV2, etc. La définition de jalon et l’attribution de valeur sont laissées au développeur.
UINT8 milestone : 3;
Si le microprogramme est destiné à une banque spécifique, le champ 2 bits prend en charge quatre banques. L’utilisation d’un registre bancaire est incluse dans le format de l’offre, car il existe des cas où les appareils cibles utilisent des régions de microprogramme bancaire.
Si tel était le cas et que l’offre était destinée à mettre à jour une banque en cours d’utilisation, le microprogramme qui implémente l’UFC sur la cible peut rejeter l’offre. Sinon, le microprogramme sur la cible qui implémente la CFU peut prendre d’autres mesures en cas de besoin.
Si la banque d’images de microprogramme n’est PAS dans la conception du microprogramme de l’utilisateur final, il est raisonnable d’ignorer ce champ (défini sur les valeurs qui conviennent, mais la valeur dans le champ banque est facultative et dépend de la façon dont le microprogramme cible implémente LFU).
UINT8 bank : 2;
La version du protocole CFU utilisé est en 4 bits.
UINT8 protocolRevision : 4;
Masque de bits correspondant à tous les HW uniques sur lesquels cette image de microprogramme peut fonctionner. Par exemple, l’offre peut signifier qu’elle peut s’exécuter sur verX de HW, mais pas sur verY de HW. La définition de bit et l’attribution de valeur sont laissées au développeur.
UINT32 hwVariantMask;
Version du microprogramme proposée.
UINT32 version;
Jeton d’octets permettant d’identifier le logiciel spécifique à l’utilisateur qui effectue l’offre. Cela est destiné à faire la différence entre les pilotes et les outils qui peuvent tous deux essayer de mettre à jour le même microprogramme en cours d’exécution. Par exemple, un pilote de mise à jour CFU peut se voir attribuer des jetons 0xA et un outil de mise à jour de développement peut être affecté 0xB. À présent, le microprogramme en cours d’exécution peut choisir d’accepter ou d’ignorer les commandes en fonction du processus qui tente de le mettre à jour.
UINT8 token;
Composant de l’appareil pour appliquer la mise à jour du microprogramme.
UINT8 componentId;
indicateurs d’interprétation de l’offre : si nous voulons que le microprogramme in situ ignore l’incompatibilité de version (plus ancien par-dessus la version plus récente), définissez le bit pour forcer Ignorer la version.
UINT8 forceIgnoreVersion: 1;
Forcer la réinitialisation immédiate est affirmé avec un bit. Si ce bit est déclaré, le logiciel hôte s’attend à ce que le microprogramme in situ provoque la réinitialisation de l’appareil. Les actions de la réinitialisation sont spécifiques à la plateforme. Le microprogramme de l’appareil peut choisir de prendre des mesures qui permutent les banques pour rendre le microprogramme fraîchement mis à jour le microprogramme in situ actif. Ou pas. Elle est laissée à l’implémentation du microprogramme. L’attente est généralement que si la réinitialisation immédiate de force est déclarée, que l’appareil fasse tout ce qui est nécessaire pour que le microprogramme à mettre à jour la nouvelle banque devienne le microprogramme actif en cours d’exécution sur l’appareil cible.
UINT8 forceImmediateReset : 1;
Dans le cas où la partie contenu de la paire offre-contenu implique plusieurs parties du contenu.
UINT8 segmentNumber;
Traitement des offres
L’API ProcessCFWUOffer accepte deux arguments :
void ProcessCFWUOffer(FWUPDATE_OFFER_COMMAND* pCommand,
FWUPDATE_OFFER_RESPONSE* pResponse)
Dans ce cas d’usage, supposons que le logiciel utilisateur envoie des octets de données au microprogramme en cours d’exécution, puis que le premier message est le message d’offre.
Le message d’offre est un message de 16 octets décrit ci-dessus (structure FWUPDATE_OFFER_COMMAND).
Ce message d’offre est les données utilisées par le microprogramme en cours d’exécution pour la disposition de l’offre.
Pendant la suppression de l’offre, le microprogramme en cours d’exécution avertit l’expéditeur en remplissant les champs dans la FWUPDATE_OFFER_RESPONSE
structure.
Interprétation de l’offre
Le microprogramme en cours d’exécution doit effectuer le suivi de son état dans le processus CFU. Il peut être prêt/en attente d’accepter une offre, au milieu d’une transaction CFU, ou en attente d’échanger des banques entre un microprogramme actif/inactif.
Si le microprogramme en cours d’exécution se trouve au milieu d’une transaction CFU, n’acceptez pas/ne traitez pas cette offre et informez l’hôte en conséquence.
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;
}
Le champ ID de composant de l’offre peut être utilisé pour signaler au microprogramme en cours d’exécution qu’une action spéciale est demandée à partir du microprogramme en cours d’exécution. Dans l’exemple de code CFU, une commande d’offre spéciale est utilisée par l’hôte pour récupérer les status du moteur CFU, si le logiciel en cours d’exécution est capable et prêt à accepter des offres 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;
}
}
Enfin, une case activée est effectuée s’il y a un échange bancaire en attente. L’échange bancaire fait référence au microprogramme qui conserve les informations indiquant s’il est toujours en cours de passage de l’application active en cours d’exécution à l’image nouvellement téléchargée.
Comment et où le basculement bancaire est effectué est une tâche spécifique à l’implémentation pour le microprogramme incorporé. Le protocole et le processus CFU permettent d’échanger des informations entre l’application utilisateur distante qui effectue la CFU et le microprogramme in situ en cours d’exécution.
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;
}
Enfin, si l’état du microprogramme en cours d’exécution n’est pas occupé, que le componentId n’est pas une commande spéciale et qu’aucun échange bancaire n’est en attente. Nous pouvons alors traiter cette offre.
Le traitement d’une offre implique, sans s’y limiter, les quatre étapes décrites ci-dessous :
Étape 1 - Vérifier la banque
Vérifiez la banque de l’application en cours d’exécution à la banque dans l’offre. Sont-ils identiques ou différents ?
Si la valeur est identique, rejetez l’offre (nous ne voulons pas remplacer l’image en cours d’exécution/active).
Sinon, continuez.
Étape 2 - Vérifier hwVariantMask
Le microprogramme en cours d’exécution vérifie dans hwVariantMask
l’offre par rapport au HW sur lequel il s’exécute. Cela permet au microprogramme incorporé de rejeter une offre si l’offre n’est pas valide pour la cible. (par exemple, si le microprogramme en cours d’exécution se trouve sur une ancienne build HW et que le nouveau microprogramme proposé est destiné à une build HW plus récente, le microprogramme en cours d’exécution doit rejeter cette offre)
Si elle n’est pas valide, rejetez l’offre.
Sinon, continuez.
Étape 3 : Vérifier la version du microprogramme
Vérifiez si la version du contenu du microprogramme proposé a une version antérieure ou plus récente que le microprogramme de l’application actuelle.
Il appartient à l’implémentation des utilisateurs de décider comment case activée quel microprogramme est supérieur à un autre et s’il faut autoriser l’utilisation du champ « forceIgnoreVersion » dans l’offre. Le développement de microprogrammes standard permet d’utiliser le champ « forceIgnoreVersion » pendant le développement du produit et dans les versions de débogage du microprogramme, mais non autorisé (ce qui n’autorise pas la mise à jour des microprogrammes plus anciens par-dessus le nouveau microprogramme) dans le microprogramme produit/version.
Si cette case activée a échoué, rejetez l’offre.
Sinon, continuez.
Étape 4 - Accepter l’offre
L’offre est bonne. Acceptez l’offre avec une réponse adaptée à la façon dont les messages et les status sont retournés par le microprogramme à l’application utilisateur distante. La « réponse » est une structure de données (une structure de données empaquetée comme indiqué dans les fichiers d’en-tête de démonstration) et ces données sont écrites dans l’application utilisateur par les moyens appropriés pour l’appareil.
Traiter le contenu
Le traitement du contenu est généralement un processus à plusieurs étapes. Les étapes multiples font référence à la capacité du microprogramme à accepter l’image du microprogramme dans des parties, également appelées « blocs » de données. Il n’est pas toujours possible d’envoyer l’image entière à la fois au microprogramme incorporé. Il est donc réaliste de s’attendre à ce que l’implémentation du protocole et du processus CFU accepte le contenu en petits morceaux.
Cette discussion utilise l’hypothèse lors de la description du processus du contenu de l’UFC.
La machine à états du traitement du contenu implique trois états.
État du traitement du premier bloc.
État du traitement du dernier bloc.
État du traitement d’un bloc entre le premier et le dernier.
Structure de la commande de contenu
Comme l’offre, le contenu a une structure avec des champs qui sont utilisés par les algorithmes CFU dans la démonstration.
typedef struct
{
UINT8 flags;
UINT8 length;
UINT16 sequenceNumber;
UINT32 address;
UINT8 pData[MAX_UINT8];
} FWUPDATE_CONTENT_COMMAND;
La structure de la commande de contenu est plus simple que la structure de l’offre. Le contenu est défini comme une séquence d’octets à écrire en mémoire. Le préambule du contenu est les champs de cette structure :
UINT8 flags
Indique si le contenu « block » est le premier, le dernier ou l’autre.UINT8 length
Marque la longueur dupData
champ. Dans le code de démonstration pour CFU, la taille limite de estpData
de 255 octets. D’autres implémentations peuvent varier la taille maximale du « bloc ».UINT16 sequenceNumber
Marque le compteur d’index dont le bloc est soumis en tant que contenu.UINT32 address
Décalage d’adresse du bloc. Dans la démonstration de la CFU de cette version, l’implémentation contient des informations prédéfinies sur l’adresse physique de chaque région d’application. Par exemple, une implémentation de microprogramme à deux banques peut avoir App1 commencer à l’adresse0x9000
et App2 commencer à l’adresse0xA0000
. Ainsi, selon la façon dont l’image du microprogramme a été préparée (S-Records), l’adresse dans le SREC peut être l’adresse physique ou un décalage. Dans tous les cas, il doit y avoir une compréhension partagée entre la préparation du contenu et les routines spécifiques d’implémentation du traitement du contenu CFU pour déterminer l’adresse physique réelle de l’endroit où écrire le bloc en mémoire. Il appartient au développeur du microprogramme d’adopter les bonnes pratiques et de vérifier les plages d’adresses valides pour chaque blog de contenu. Par exemple, le code CFU illustre une case activée effectuée si App1 (destiné à0x9000
) a peut-être des adresses qui se chevauchent dans App2, etc.UINT8 pData[MAX_UINT8]
- Il s’agit des octets bruts du bloc d’image du microprogramme. Dans l’application utilisateur, vous devez veiller à ne placerlength
que des octets dans le flux d’octets complet du bloc de contenu.
Aucun champ de bits n’est utilisé dans la structure de contenu conformément à la démonstration CFU à partir du code fourni.
Premier bloc
Le premier bloc démarre le téléchargement du contenu du microprogramme. Le microprogramme en cours d’exécution tente d’écrire le bloc dans la mémoire non volatile. Bien sûr, le contenu « bloc » contient des informations sur l’emplacement en mémoire du bloc à écrire, la quantité de données à écrire et d’autres champs.
Chaque appareil cible componentID est différent et il existe plusieurs méthodes pour conserver les données en mémoire. Par exemple, un componentId peut nécessiter l’écriture dans un flash interne, un autre componentId peut écrire dans un flash SPI externe ou un autre peut utiliser le protocole I2C d’un autre ic pour mettre à jour son image. La démonstration incluse dans ce document met en évidence l’utilisation d’une fonction appelée ICompFwUpdateBspWrite
que chaque microprogramme unique doit implémenter avec connaissance des fonctions d’E/S de mémoire non volatile sous-jacentes de la cible pour laquelle elle a été conçue.
Tout autre bloc sauf le premier ou le dernier
Le processus d’acceptation de nouveaux blocs se poursuit lorsque l’application utilisateur remet un autre bloc, avec de nouveau des métadonnées dans le message pour l’adresse où le bloc doit être écrit, le nombre d’octets contenus et d’autres champs.
Le microprogramme in situ traite cela comme un scénario de premier bloc.
Toutefois, il convient de noter qu’à tout moment le système ne parvient pas à capturer et à conserver le bloc en mémoire, il appartient au microprogramme in situ de répondre avec un code d’échec.
Le dernier bloc
Le dernier bloc présente un défi uniquement si le microprogramme in situ doit effectuer des tâches pour valider l’image qui vient d’être écrite en mémoire.
Tout d’abord, le dernier bloc est écrit en mémoire.
Ensuite, au minimum, une case activée CRC doit être effectuée entre les données déjà écrites en mémoire (du premier au dernier bloc) par rapport au champ CRC du dernier bloc. Il est laissé à chaque microprogramme d’implémentation de savoir comment acquérir le CRC pour l’image téléchargée.
N’oubliez pas que l’exécution de la case activée CRC prend du temps. Contrairement au flux normal de l’exécution de l’UFC pour l’offre et la soumission de bloc. Le dernier bloc de soumission, s’il comprend un case activée crc, aura un certain retard en ce qui concerne le fait que le case activée de la CRC examine potentiellement une grande partie de la mémoire. Selon l’appareil cible et d’autres facteurs, cela peut ne pas être un problème.
Important
La case activée CRC de l’image entrante est facultative et peut être commentée. Toutefois, les meilleures pratiques devraient être mises en place pour adopter au moins cette case activée. Il est vivement recommandé qu’à ce stade du processus CFU, d’autres mesures soient prises pour garantir l’intégrité de l’image téléchargée. Certaines de ces actions peuvent inclure la vérification d’une partie « signée » de l’image et/ou case activée chaînes de certificats de confiance ou d’autres meilleures pratiques pour garantir une image de microprogramme sécurisée. Celles-ci sont laissées au développeur du microprogramme.
Nettoyer après le dernier bloc
Maintenant que le dernier bloc est écrit et que le case activée CRC est terminé, le microprogramme peut répondre avec un échec si une partie de la validation a échoué.
Sinon, on s’attend à ce que le processus CFU dans le microprogramme réponde avec une status réussie.
Réinitialisation forcée cochée
L’indicateur de réinitialisation forcée dans l’offre est utilisé pour déterminer si le MCU de la cible doit subir une réinitialisation (réinitialisation définie par l’utilisateur).
En règle générale, lorsqu’une réinitialisation est forcée, l’intention est d’amener le MCU à effectuer une réinitialisation afin d’entraîner le basculement de la banque d’applications. La mise à jour des variables persistantes pour indiquer l’image de microprogramme à démarrer lors de la réinitialisation est laissée au développeur du microprogramme.