Encore un fois, c'est trop long ;-) (et je n'ai pas relu...)
Il y a plusieurs aspects, au moins l'aspect technique ("comment on peut changer une fonctionnalité du satellite") et l'aspect "stratégique" ("est-ce qu'on veut changer une fonctionnalité du satellite ou la corriger")
Sur l'aspect stratégique, généralement l'idée c'est qu'on veut avoir la possibilité technique de changer quelque chose, mais qu'on espère ne jamais l'utiliser. Et si on peut, donc, on ne patche pas (du coup, ça veut dire qu'avant le lancement, on essaye de valider en profondeur tous les aspects, via de la simulation principalement)
Mais j'imagine que l'aspect technique est plus intéressant. Du coup, on avait plusieurs possibilités techniques qu'ils fallait valider au plus haut niveau, parce que même si on espérait ne jamais les utiliser (à cause de la stratégie), le logiciel est la seule chose qu'on peut changer une fois en vol.
Intro : La mémoire
Bon, en fait, les mémoires. Que ça soit le code ou les données, elles doivent forcément être en mémoire quelque part. Ou plusieurs endroits. Du coup, résumé (approximatif, c'était il y a longtemps) de ce qu'on avait :
des ROM. Read only memory (en théorie, mais souvent des EEPROM, donc qu'on peut mettre en mode "écriture autorisée). Normalement même si on coupe l'alimentation en électricité, elles gardent leurs valeurs.
des RAM. Pas read only. Potentiellement si on coupe le courant, elles perdent leur données (*)
Par contre, on n'avais pas une ROM et une RAM, mais plusieurs de chaque.
Chaque calculateur avait sa ROM (avec le binaire) et sa RAM (le binaire est copié au boot dedans, et les données calculées sont là). Ces deux là n'étaient accessible que par le calculateur en question (donc ROM1 <-> CPU1 <-> RAM1, et pareil pour le 2, pas d'accès croisés). La théorie c'est que les deux calculateurs ont le même binaire, mais rien n'oblige à le faire, on aurait pu avoir deux binaires complètement différent, par contre, on avait de la redondance froide, donc un seul calculateur allumé.
On avait aussi deux RAM appelées SGRAM (1 et 2) pour safe guard memory. Par défaut, pas grand chose dedans, sauf les données qui avaient besoin de survivre à un reboot. Là par contre les deux calculateurs pouvaient accéder aux deux SGRAM. Les données survivaient à un reboot (je ne sais plus si c'est parce qu'elles étaient tout le temps alimentées, ou si elles survivaient à un cycle off/on) C'est vraiment une ram par contre, certaines variables étaient directement dans cette RAM.
Et aussi une (ou deux?) SG ROM. Pareil, les deux calculateurs y ont accès. De mémoire, on avait rien dedans, sauf des tables de patch, à appliquer (ou non) au boot. Par défaut, on fait un logiciel parfait ( ;-) ) donc il n'y a pas de patch à appliquer, donc c'est vide.
Voila, en résumé : des mémoires "volatiles" et des mémoires permanentes, des mémoires spécifiques à un calculateur et des mémoires accessibles aux deux.
Reprenons les possibilités de changement de logiciel.
Possibilité 1: Le paramétrage.
Je ne sais pas si ça compte vraiment dans la catégorie "mise à jour logicielle", mais il y avait toute une série de paramètres qui sont modifiables par télécommande dédiée. Par exemple, si on a besoin du moment d'inertie du satellite pour contrôler la rotation, on le connaît plus ou moins au lancement (on a le plan du satellite, la masse de chaque élément, donc physique + maths --> J). Sauf que 1) peut être qu'on a une erreur (et on peut l'estimer quand le satellite est en vol si il ne se comporte pas comme attendu)
Et au cours de la vie du satellite, il va changer (quand on utilise les "thruster", on brule du carburant, donc la répartition de masse change, donc on peut vouloir le mettre à jour.
Donc dans la liste de tous les paramètres du satellite, certains doivent pouvoir être changés. Donc on va les classer en catégories : "ne changera jamais" (pi, par exemple, normalement, ça ne change pas), "on doit pouvoir le changer". Cette catégorie va elle même être redivisée, par exemple entre "c'est important d'avoir la bonne valeur" ou "c'est juste un nice to have". Parce que si c'est vraiment important, il faut que quand on reboot on ait la bonne valeur, pour les autres, on peut juste faire une update "quand le logiciel tourne".
En fonction de la catégorie, la valeur peut être simplement en ROM, dans la RAM (et potentiellement updatée par commande "jusqu'au prochain reboot"), en SGRAM (résiste au reboot)
Possibilité 2: les free functions
Evoqué dans mon texte initial, on avait 3 tâches périodiques (désactivées par défaut) qui ne faisait rien. En gros, ça ressemblait à
Et on vient rajouter une fonction... en écrivant ailleurs dans la mémoire, là où c'est libre. On choisit la mémoire qu'on veut (si on veut jusqu'au prochain reboot, c'est simple, on écrit en RAM. Si on veut que ça survive à un reboot, on le met dans les tables de patch en SGROM... faut que je décrive le mécanisme plus bas.
Donc pour activer une free fonction :
écrire le code (compilé) dans une zone de mémoire libre, à l'adresse 0x42 par exemple. Et à la fin du code de la fonction, on met un jump .free_function_end
vérifier qu'on a bien écrit correctement ce qu'on voulait à l'adresse 0x42 pour être sûr en dumpant la mémoire
changer (patcher) un des noop au début de la free_function pour faire un jump 0x42 (vérifier qu'on a bien écrit)
modifier le paramètre qui active la tâche de la free function (par défaut, elle n'est pas active)
Et voila.
Possibilité 3 : Changer une fonction existante
Imaginons qu'on a une fonction avec un bug. On veut la corriger. On pourrait venir l'écraser directement en mémoire, mais
ça ne marche que si ne nouveau code rentre là
si c'est une fonction critique et que le patch rate (et plus la fonction est longue, plus la possibilité est grande) c'est un peu la merde.
Du coup, la méthode ça serait plutôt similaire à ce qu'on fait pour la free fonction.
on écrit la version corrigée dans une zone libre
on vérifie
on remplace l'appel à la fonction original par un appel à celle qu'on vient d'écrire (au lieu de call addresse(function_originale), on fait call addresse(function_patchée).
Alternativement, si c'est une fonction qui est appelée à plusieurs endroit, on peut, à la place du point trois, venir modifier la première instruction de la fonction originale par un jump addresse(function_patchée)
Interlude : Les tables de patch
Les possibilités 2 et 3, ça marche très bien en RAM, donc pour la vie courante du logiciel, jusqu'au reboot. On pourrait faire aussi le même patch en ROM, mais
on ne veut pas mettre la ROM en mode écriture possible (radiations, erreurs, ... et ce serait permanent) ; et
ça ne concernerait qu'un seul calculateur à la fois. Il faudrait donc rebooter sur l'autre si on voulait appliquer le même patch dans l'autre ROM
C'est là qu'interviennent les tables de patch en SGROM. Au boot du calculateur, il se passe plusieurs choses:
la ROM est copiée en RAM
quelque chose fait démarrer le logiciel à l'adresse 0xB00T (oui, T n'est pas un chiffre hexa... :-p)
la fonction de code "boot_logiciel()" de l'adresse 0xB00T démarre
Cette fonction va aller lire la table de patch. C'est un tableau de structure, quelque chose comme
struct table_de_patch {
enum calculateur; // (aucun, CPU1, CPU2, BOTH)
bitfield actif_in_mode; // 0b xxxxx
int start_addresse;
int longeur;
int data[];
}
(plutôt qu'un tableau, c'est probablement plutôt une linked list, vu que la taille doit varier, mais bon)
Pour chacun des éléments du tableau, au boot, on va regarder si on doit "patcher" la RAM qui vient d'être copiée depuis la rom. Si on est sur le bon calculateur (en fonction de l'enum), que le patch est censé être appliqué dans ce mode (plusieurs mode, c'est géré hors du logiciel, on commence à 2, à chaque anomalie grave ça augmente, si on arrive à 5, c'est grave.) alors le patch est appliqué.
Si je reprends l'exemple de la free function, plutôt que de l'appliquer en RAM directement, je peux mettre deux choses dans la table de patch :
le code que je mettais à l'adresse 0x42
le jump dans le code de la free function
je configure les deux pour être appliqué dans les modes 2, 3, 4 et sur les deux calculateurs.
Et voila, chaque fois que je reboot, la free fonction existe, même si je n'ai pas patché le "vrai" binaire, elle est rajoutée "automatiquement" par le code lui même.
Possibilité 4 : Changer tout le logiciel
Bon, préliminaires :
Non, la table de patch ne peut pas faire ça, on a pas assez de place.
C'est de la théorie, c'était possible, testé, mais pas documenté pour le client. La NASA le fait en vrai (pas sûr qu'ils le fasse comme nous)
Imaginons que je veux changer tout le code, entièrement, comment je fais ? En théorie, c'est simple : mon code tourne en RAM, je peux changer la ROM. Petit problème, ça prend du temps, si on reboote au milieu... le calculateur est mort pour toujours.
Solution ? Facile, j'ai 2 Mo de ROM, donc si "Logiciel Original" + "Nouveau Logiciel" <= 2Mo de ROM, je mets le logiciel là où j'ai la place dans la ROM. Et une fois que j'ai vérifier que c'est bien écrit, au lieu de booter à l'adresse 0xB00T je boot à l'adresse 0xNOUVEAU_B00T.
Sur les prédécesseurs de ce satellite en particulier, notre logiciel en ROM faisait moins de 1Mo, donc ça marchait. Sur ce projet en particulier, ça faisait un truc du genre 1,2Mo, donc non. Mais pas de problème, il "suffit" de ne pas avoir 2 logiciels, mais 3.
logiciel original : 1.2 Mo
logiciel avec "juste de quoi patcher et survivre dans l'espace" : moins de 2Mo - 1.2 Mo
logiciel final : 1.2 Mo
Adapter les tailles, si jamais le final est plus grand...
On applique deux fois la procédure de changement de logiciel. Et voila. Ca marche sur le banc de test. En vrai ça n'existe pas.
Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.
[^] # Re: OTA dans l'espace
Posté par 2PetitsVerres (Mastodon) . En réponse au journal "It works on my satellite". Évalué à 10.
Sommaire
Encore un fois, c'est trop long ;-) (et je n'ai pas relu...)
Il y a plusieurs aspects, au moins l'aspect technique ("comment on peut changer une fonctionnalité du satellite") et l'aspect "stratégique" ("est-ce qu'on veut changer une fonctionnalité du satellite ou la corriger")
Sur l'aspect stratégique, généralement l'idée c'est qu'on veut avoir la possibilité technique de changer quelque chose, mais qu'on espère ne jamais l'utiliser. Et si on peut, donc, on ne patche pas (du coup, ça veut dire qu'avant le lancement, on essaye de valider en profondeur tous les aspects, via de la simulation principalement)
Mais j'imagine que l'aspect technique est plus intéressant. Du coup, on avait plusieurs possibilités techniques qu'ils fallait valider au plus haut niveau, parce que même si on espérait ne jamais les utiliser (à cause de la stratégie), le logiciel est la seule chose qu'on peut changer une fois en vol.
Intro : La mémoire
Bon, en fait, les mémoires. Que ça soit le code ou les données, elles doivent forcément être en mémoire quelque part. Ou plusieurs endroits. Du coup, résumé (approximatif, c'était il y a longtemps) de ce qu'on avait :
Par contre, on n'avais pas une ROM et une RAM, mais plusieurs de chaque.
Chaque calculateur avait sa ROM (avec le binaire) et sa RAM (le binaire est copié au boot dedans, et les données calculées sont là). Ces deux là n'étaient accessible que par le calculateur en question (donc ROM1 <-> CPU1 <-> RAM1, et pareil pour le 2, pas d'accès croisés). La théorie c'est que les deux calculateurs ont le même binaire, mais rien n'oblige à le faire, on aurait pu avoir deux binaires complètement différent, par contre, on avait de la redondance froide, donc un seul calculateur allumé.
On avait aussi deux RAM appelées SGRAM (1 et 2) pour safe guard memory. Par défaut, pas grand chose dedans, sauf les données qui avaient besoin de survivre à un reboot. Là par contre les deux calculateurs pouvaient accéder aux deux SGRAM. Les données survivaient à un reboot (je ne sais plus si c'est parce qu'elles étaient tout le temps alimentées, ou si elles survivaient à un cycle off/on) C'est vraiment une ram par contre, certaines variables étaient directement dans cette RAM.
Et aussi une (ou deux?) SG ROM. Pareil, les deux calculateurs y ont accès. De mémoire, on avait rien dedans, sauf des tables de patch, à appliquer (ou non) au boot. Par défaut, on fait un logiciel parfait ( ;-) ) donc il n'y a pas de patch à appliquer, donc c'est vide.
Voila, en résumé : des mémoires "volatiles" et des mémoires permanentes, des mémoires spécifiques à un calculateur et des mémoires accessibles aux deux.
Reprenons les possibilités de changement de logiciel.
Possibilité 1: Le paramétrage.
Je ne sais pas si ça compte vraiment dans la catégorie "mise à jour logicielle", mais il y avait toute une série de paramètres qui sont modifiables par télécommande dédiée. Par exemple, si on a besoin du moment d'inertie du satellite pour contrôler la rotation, on le connaît plus ou moins au lancement (on a le plan du satellite, la masse de chaque élément, donc physique + maths --> J). Sauf que 1) peut être qu'on a une erreur (et on peut l'estimer quand le satellite est en vol si il ne se comporte pas comme attendu)
Et au cours de la vie du satellite, il va changer (quand on utilise les "thruster", on brule du carburant, donc la répartition de masse change, donc on peut vouloir le mettre à jour.
Donc dans la liste de tous les paramètres du satellite, certains doivent pouvoir être changés. Donc on va les classer en catégories : "ne changera jamais" (pi, par exemple, normalement, ça ne change pas), "on doit pouvoir le changer". Cette catégorie va elle même être redivisée, par exemple entre "c'est important d'avoir la bonne valeur" ou "c'est juste un nice to have". Parce que si c'est vraiment important, il faut que quand on reboot on ait la bonne valeur, pour les autres, on peut juste faire une update "quand le logiciel tourne".
En fonction de la catégorie, la valeur peut être simplement en ROM, dans la RAM (et potentiellement updatée par commande "jusqu'au prochain reboot"), en SGRAM (résiste au reboot)
Possibilité 2: les free functions
Evoqué dans mon texte initial, on avait 3 tâches périodiques (désactivées par défaut) qui ne faisait rien. En gros, ça ressemblait à
Et on vient rajouter une fonction... en écrivant ailleurs dans la mémoire, là où c'est libre. On choisit la mémoire qu'on veut (si on veut jusqu'au prochain reboot, c'est simple, on écrit en RAM. Si on veut que ça survive à un reboot, on le met dans les tables de patch en SGROM... faut que je décrive le mécanisme plus bas.
Donc pour activer une free fonction :
0x42par exemple. Et à la fin du code de la fonction, on met unjump .free_function_end0x42pour être sûr en dumpant la mémoirejump 0x42(vérifier qu'on a bien écrit)Et voila.
Possibilité 3 : Changer une fonction existante
Imaginons qu'on a une fonction avec un bug. On veut la corriger. On pourrait venir l'écraser directement en mémoire, mais
Du coup, la méthode ça serait plutôt similaire à ce qu'on fait pour la free fonction.
call addresse(function_originale), on faitcall addresse(function_patchée).Alternativement, si c'est une fonction qui est appelée à plusieurs endroit, on peut, à la place du point trois, venir modifier la première instruction de la fonction originale par un
jump addresse(function_patchée)Interlude : Les tables de patch
Les possibilités 2 et 3, ça marche très bien en RAM, donc pour la vie courante du logiciel, jusqu'au reboot. On pourrait faire aussi le même patch en ROM, mais
C'est là qu'interviennent les tables de patch en SGROM. Au boot du calculateur, il se passe plusieurs choses:
0xB00T(oui, T n'est pas un chiffre hexa... :-p)0xB00TdémarreCette fonction va aller lire la table de patch. C'est un tableau de structure, quelque chose comme
(plutôt qu'un tableau, c'est probablement plutôt une linked list, vu que la taille doit varier, mais bon)
Pour chacun des éléments du tableau, au boot, on va regarder si on doit "patcher" la RAM qui vient d'être copiée depuis la rom. Si on est sur le bon calculateur (en fonction de l'enum), que le patch est censé être appliqué dans ce mode (plusieurs mode, c'est géré hors du logiciel, on commence à 2, à chaque anomalie grave ça augmente, si on arrive à 5, c'est grave.) alors le patch est appliqué.
Si je reprends l'exemple de la free function, plutôt que de l'appliquer en RAM directement, je peux mettre deux choses dans la table de patch :
0x42jumpdans le code de la free functionEt voila, chaque fois que je reboot, la free fonction existe, même si je n'ai pas patché le "vrai" binaire, elle est rajoutée "automatiquement" par le code lui même.
Possibilité 4 : Changer tout le logiciel
Bon, préliminaires :
Imaginons que je veux changer tout le code, entièrement, comment je fais ? En théorie, c'est simple : mon code tourne en RAM, je peux changer la ROM. Petit problème, ça prend du temps, si on reboote au milieu... le calculateur est mort pour toujours.
Solution ? Facile, j'ai 2 Mo de ROM, donc si "Logiciel Original" + "Nouveau Logiciel" <= 2Mo de ROM, je mets le logiciel là où j'ai la place dans la ROM. Et une fois que j'ai vérifier que c'est bien écrit, au lieu de booter à l'adresse
0xB00Tje boot à l'adresse0xNOUVEAU_B00T.Sur les prédécesseurs de ce satellite en particulier, notre logiciel en ROM faisait moins de 1Mo, donc ça marchait. Sur ce projet en particulier, ça faisait un truc du genre 1,2Mo, donc non. Mais pas de problème, il "suffit" de ne pas avoir 2 logiciels, mais 3.
Adapter les tailles, si jamais le final est plus grand...
On applique deux fois la procédure de changement de logiciel. Et voila. Ca marche sur le banc de test. En vrai ça n'existe pas.
Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.