Journal "It works on my satellite"

Posté par  (Mastodon) . Licence CC By‑SA.
135
3
déc.
2025

Sommaire

Ce journal raconte un vieux bug que j'ai eu sur un satellite. L'identification, la reproduction, la correction. C'est le bug qui m'a le plus intéressé/marqué dans ma carrière (jusqu'ici), et du coup peut être que ça vous intéressera aussi.

L'appel

Il y a bien longtemps, dans une galaxie lointaine. Ah non pardon. Un long weekend de 14 juillet, sur une plage, je reçois un coup de fil : "Un des satellites a rebooté, à cause d'une erreur logicielle, est-ce que tu es dispo pour venir comprendre ce qu'il s'est passé ? A priori il fonctionne toujours, mais il est passé tout seul sur le calculateur redondant."

Quelques mois avant, on a lancé une première grappe de 6 satellites, d'autres lancements sont prévus pour compléter une constellation dans les mois/années à venir. Comme tout marche bien depuis des mois, personne de l'équipe logiciel de bord n'est d'astreinte. Sur ces satellites j'étais surtout sur la partie validation. En gros ce jour là pour moi ce n'était pas possible, mais j'ai été le lendemain, un samedi ou dimanche.

L'objectif et les moyens de débug

Si nos managers nous ont appelé, c'est parce quand un satellite bugue en prod (on va dire en vol, plutôt), c'est comme pour n'importe quel autre logiciel, des gens veulent des réponses à des questions comme :

  • pourquoi ?
  • est-ce que c'est grave ?
  • est-ce que ça va se reproduire ?
  • comment on corrige ?

Par contre, les moyens sont potentiellement différents de ce que vous avez dans d'autres environnement (ou pas, j'imagine que ça dépend des gens) Ce qu'on a :

  • le code
  • la doc
  • des bancs de tests (avec le même hardware pour le calculateur)
  • des gens
  • un tout petit peu de contexte logiciel sauvegardé au moment de l'erreur (j'y reviens)
  • la télémétrie avant l'anomalie (tout allait bien)
  • la télémétrie après l'anomalie (tout va bien, mais on est passé du mode hardware 2 au mode 3. En gros c'est le même, sauf qu'on utilise certains équipement "redondants" au lieu du "nominal", dont le calculateur)

Premier élément, qui a mené au fait que c'est nous (du logiciel) qui avons été appelés, c'est que le hardware qui gère le mode (2 -> 3) peut changer de mode pour plusieurs raisons, mais il sait pourquoi il le fait. Et la raison c'est "le logiciel m'a dit de le faire". Donc ça vient de nous.

L'analyse

Comme tout va bien, on va regarder le contexte sauvegardé. Ce n'est pas un core dump qu'on peut passer à gdb, mais ça contient quelques infos :

  • le code de l'erreur ILLEGAL CPU INSTRUCTION
  • le Program Counter %pc qui nous donne l'adresse de l'instruction exécutée au moment de l'erreur
  • l'adresse de la prochaine instruction à exécuter %npc (ici c'est l'adresse juste après %pc, rien de surprenant)
  • une copie des registres (bon, on ne va pas en avoir besoin, du coup je ne vous fais pas un cours sur SPARC et ses registres tournant, de toute façon j'ai oublié. On pourrait probablement les utiliser pour récupérer partiellement la pile d'appel, on l'a surement fait)
  • la date et l'heure (super info utile. Enfin, ça correspond à notre anomalie, j'imagine que c'est pour ça qu'on l'avait)
  • surement d'autres choses, mais pas utiles pour la suite.

Problème résolu donc ? on est à l'adresse %pc, on l'exécute et le CPU nous dit que l'instruction n'est pas légale. Qu'est-ce qu'il y a ici ? Une instruction légale, quels que soit la valeur des registres. Pareil pour un peu plus haut et un peu plus bas, rien qui provoque cette erreur. Du coup, qu'est-ce qu'il s'est passé ?

On est dans l'espace, donc l'explication facile (dès qu'on n'explique pas un truc) : l'instruction a du avoir un Single Event Upset (SEU), un bit flip. Ca a transformé une instruction légale en instruction illégale. C'est facile ? Sauf que non, on est dans l'espace, du coup on a tout un mécanisme de protection contre les SEU. C'est pas infaillible (par exemple si on a deux bits inversé, on ne peut pas corriger) mais ce n'est pas la bonne signature. Si c'était ça, ça dirait DOUBLE EDAC ERROR, pas ILLEGAL CPU INSTRUCTION.

Donc la cause de l'anomalie n'est pas un SEU.

EDAC / Protection contre les SEU

Je suis sûr que vous êtes intéressé, donc je vais vous décrire la protection contre les bit flips. C'est un mix de hardware/software (en plus d'avoir une boite autour qui diminue la probabilité). En mémoire (RAM, ROM) pour 4 octets de données "utiles", on a 5 octets sont écrits. Pour l'octet supplémentaire, il est calculé via un code d'EDAC, ce qui fait que si un bit sur les 40 change, on peut à la fois savoir qu'un bit a changé, et on peut reconstruire la valeur correcte. Si deux bits changent (ou plus, mais il y a une limite), on peut détecter l'erreur mais pas la corriger (le DOUBLE EDAC ERROR mentionné plus, c'est ça)

C'est complètement transparent vu du logiciel (code source, ou assembleur), tout ça est calculé par le hardware. Quand on écrit en mémoire 0x12345678 il calcule le code et écrit 0x12345678XY avec la bonne valeur de X et Y. Quand on lit, pareil, le hardware commence par lire 0x12345678XY, calcule le checksum sur les 4 octets, si c'est le bon, il nous donne 0x12345678.

Là où ça se complique, c'est quand il y a un changement. Disons qu'on a maintenant 0x02345678XY. (1 --> 0). Il se passe deux choses ici :

  1. le hardware dit au logiciel 0x12345678 (il corrige, mais uniquement la valeur envoyé au software. Pas la valeur enregistrée en mémoire)
  2. il émet un signal SINGLE EDAC ERROR.

C'est là que le logiciel intervient, dans le point 2. Ce signal est lié à une trap qui corrige la mémoire. Schématiquement c'est lié à une fonction qui ressemble à ceci (en assembleur SPARC en vrai, mais j'ai tout oublié)

                        ; adresse vient du contexte, c'est l'adresse qui a été lue en dernier, qui a généré la trap
disable_edac_trap:      ; Désactiver la trap. Sinon on déclencherait la trap depuis la trap
load [adresse], reg     ; Lire 4 octets (lecture = correction auto)
enable_edac_trap:       ; 
store reg, [adresse]    ; Réécrire la valeur corrigée

On lit la valeur, c'est corrigé vu du logiciel par le hardware, on réécrit la valeur, tout est corrigé.

Cette trappe peut être déclenchée par n'importe quelle instruction qui lit de la mémoire (ou par le fait de charger une instruction elle même depuis la mémoire), et on a même une tache de fond (plus basse priorité, qui tourne en permanence quand il reste du temps de calcul disponible) qui fait

// en gros. En vrai légèrement plus compliqué
void background_task(void) {
    int address = MEMORY_START;
    volatile int value;
    while (1) {
        value = *address;       // si il y a un bit flip en mémoire, ce sera corrigé par la trap
        address += 4;
        if (address >= MEMORY_END) {
            address = MEMORY_START;
        }
    }
}

L'idée de cette fonction c'est de lire la mémoire régulièrement. Si on ne faisait pas ça, peut être que certaines cases mémoires auraient deux bit flips, car pas corrigé après le premier si on ne lit pas la mémoire avant qu'un autre arrive. Ce n'est pas très fréquent d'avoir des bits flips, mais sur les 6 satellites, en cumulé, on en détecte quelque uns par jour.

L'hypothèse

De retour à la case départ donc. On exécute apparemment l'instruction stockée dans %pc, valide. Et le CPU nous dit qu'elle est invalide, mais clairement elle est valide. On tourne en rond, on est samedi ou dimanche, fin d'après midi, et le satellite, lui aussi il tourne en rond, sans problèmes. Du coup quelqu'un a l'idée de dire "bon, on ne résoudra pas ça aujourd'hui. On se revoit lundi ?". On rentre, je bois un verre avec mes collocs (enfin, je suppose. C'était une activité habituelle pour un weekend, ça, au moins)

Retour au bureau, et là (surement plus tard, pas lundi 9h) on a David (un collègue) qui propose : "Comme clairement %pc est valide, est qu'on exécute quelque chose d'invalide, est-ce qu'on est sûr qu'on a bien enregistré %pc?". On vérifie, le code qui fait ça a l'air correct. En plus le contexte général, ce qu'il y a dans les registres est correct. Toujours David "Ok, le logiciel est correct, mais est-ce qu'on est sûr que %pc c'est bien toujours l'instruction qu'on exécute ?".

Donc on vérifie, par acquis de conscience et on remarque que non, pas nécessairement. Si on est dans une trap, le %pc qu'on enregistre pointe vers l'instruction qui a provoqué la trap, pas l'instruction de la trap qu'on exécute. Bon, ok, ça ne nous avance pas nécessairement (mais si j'en parle… )

Nouvelle question donc : Si on est à %pc, quelles sont les traps qui peuvent s'exécuter ? Il y a plein de possibilités, la plupart viennent de causes extérieurs (timer hardware, plein d'autres évènements extérieurs) et potentiellement aussi la trap de l'EDAC si on lit une valeur (et l'instruction à %pc lit une valeur).

Donc techniquement, on pourrait aussi être n'importe où dans le code (assembleur) de toutes les traps. Avant on cherchait pourquoi c'était illégal d'exécuter %pc, maintenant on cherche pourquoi ça serait illégal d'exécuter %pc ou n'importe quelle ligne d'une trap active/activable à ce moment là.

Chez moi, ça marche

Sauf que le code des traps, c'est pas nous qui l'avons écrit. C'est bien du code qui vient de l'entreprise, mais il existe depuis plusieurs années, est utilisé sur le même processeur depuis plusieurs années, et il a plusieurs dizaines d'année de vol (cumulé, en additionant les satellites) sans problème.

En suivant les principes bien connu du développement logiciel, si on utilise un logiciel sur étagère, pas besoin de le valider (surtout ça coute de l'argent. Cela dit même si on avait essayé, je ne pense pas qu'on aurait trouvé de problème), vu qu'il marche. Par acquis de conscience on demande, et on nous répond "bah chez nous ça marche" (la légende veut qu'une histoire similaire soit à l'origine de Docker, je ne sais pas si c'est vrai, mais le fameux "it works on my desktop, ship my desktop" …)

Vous avez peut être lu le titre de l'article, donc vous imaginez où je vais. On se demande "ok, pourquoi ça marche pour eux, et pas pour nous ?" Quelles sont les différences ?

  • on est sur le même CPU/MCU (donc non, c'est pas ça)
  • on a changé de compilateur pour une nouvelle version (mais 1. c'est un compilateur "certifié", et 2. les traps sont en assembleur…)
  • on est en orbite plus basse, et on a plus de SEU (mais même, quand on regarde leur historique, ils en ont beaucoup aussi, et en cumulé, beaucoup plus. Après on a peut être pas de chance ?)

L'erreur

Ok, on a changé de compilateur, les traps sont en assembleur, mais le reste du code est dans un langage bien plus courant (non, je rigole, en vrai c'est en Ada…), peut être que l'interaction entre les traps et le reste du code a changé ?

Pourquoi est-ce qu'on a décidé de changer de compilateur ? Ah pour des histoires de taille mémoire (640 kB should be enough? On avait même plus, genre 2 Mo de ROM, 4 Mo de RAM, large… ou pas). D'ailleurs, au moment du changement, on en a profité pour faire quelques optimisations. Non pas des flags genre -O1 ou -O2. Plus des choses sur le layout mémoire, on a ajouté __attribute__((packed)) qui est supporté, on a un peu changé le linker script, …

Par exemple, le packed, ça nous permet de gagner de la place, avant toutes les variables étaient alignées sur une adresse multiple de 4, que ça soit un nombre sur 4 octets, ou un char de un octet, ils prenaient au moins 4 octets. Maintenant, on a mis les data types multiple de 4 au début de la structure, bien alignés, puis les types qui prenent deux octets, on en mets deux dans quatre octets (au lieu d'un et de gacher deux octets pour rien), puis les types de un octect, on en met 4.

D'ailleurs, par exemple, l'instruction à %pc, elle charge une donnée d'un seul octet qui est dans une adresse du type XXX+3, où X est un multiple de 4. C'est pas illégal de faire ça (donc non, toujours pas d'instruction illégale ici)

Du coup, là, c'est là où David revient (dans mon souvenir en tout cas, ça venait beaucoup de lui, mais on était beaucoup à échanger sur le sujet). "Ok, %pc lit une donnée non alignée, et il le fait correctement. Mais si il y a un bit flip, il se passe quoi ?. Bah rien, EDAC détectée, trap, on exécute le code assembleur qui marche sur les autres satellites.

Ah oui, mais non. Si on lit un octet, on peut lire XXX+3, mais si on lit 4 octets, c'est interdit. Il faut lire une adresse multiple de 4. Et donc on a une EDAC, et quand on rentre dans la trap

                        ; adresse == XXX+3
disable_edac_trap:      ; 
load [adresse], reg     ; Lire 4 octets
enable_edac_trap:       ; 
store reg, [adresse]    ; 

Ah oui, mais non. load ça lit 4 octets, c'est illégal de lui passer une adresse non multiple de 4, c'est une illegal instruction. Donc ça pourrait être ça :

  1. bit flip sur les quatres octets situés à XXX (l'EDAC est toujours calculé sur 4 octets d'une adresse alignée, même si on lit décalé)
  2. on rentre dans la fonction qui contient %pc
  3. on lit un octet à XXX+3
  4. ça déclenche la trap
  5. la trap essaye de lire 4 octets à XXX+3
  6. ILLEGAL CPU INSTRUCTION, allez en prison sans passer par la case départ

La reproduction

Sur le papier, ça marche. On peut même faire un petit logiciel sur le banc, qui fait juste un load [XXX+3], reg et qui génère une ILLEGAL CPU INSTRUCTION. Mais évidemment nos managers (et notre client) voudrait un peu plus qu'un "sur le papier, c'est ça, trust me bro".

Donc la question "c'est possible de reproduire exactement comme dans l'espace, plutôt que de juste exécuter une instruction illégale à la main?". Avec le vrai logiciel qui était dans l'espace, pas un logiciel de test ?

Bien sûr, il suffit d'attendre d'avoir un bit flip, sur le banc, juste au bon endroit, au bon moment. Vous avez combien de siècles devant vous ? Ou alors est-ce qu'on peut mettre le banc à côté d'un réacteur nucléaire ? Ca devrait accélérer les choses (du bon côté du mur de confinement. Ici, "bon", ça veut dire mauvais pour les humains)

On va quand même regarder si on peut provoquer un bit flip autrement. Bon, a priori, en interne, au logiciel, on ne sait pas comment faire. La doc du processeur (qui vient avec l'edac) ne nous aide pas non plus. On demande à ceux qui nous ont dit que "chez eux, ça marche" qui nous répondent que la trap de l'edac, ils ne l'ont jamais testé, c'est juste une revue de code.

Bon, on envoie quand même un email au fabriquant du proc, au cas où. Réponse rapide "je reviens vers vous dès que je sais". Quelque jours (2, 3 semaines ?) plus tard : "Ah oui, c'est possible. D'ailleurs c'est documenté. Page WSYZ sur 5000, il y a **un* paragraphe qui explique comment faire*".

Le TL/DR du paragraphe : Il est possible de désactiver l'EDAC en écriture. Par contre il faut faire des choses spécifiques, donc on a pas de commande prévue pour le faire "simplement" depuis l'extérieur, il faudrait une nouvelle fonction.

void generer_bit_flip(int address, int valeur) {
    *address = valeur;                                  // écrit la valeur correcte avec l'edac normal
    manipulate_specific_register_to_disable_edac();     // on a du écrire la fonction, c'est pas aussi simple
    *address = valeur ^ 0x00000001;                     // écrit la valeur avec un bit changé, mais sans changer le checksum enregistré
    manipulate_specific_register_to_enable_edac();
}

Ca tombe bien, le logiciel qui est dans l'espace a deux fonctionalités qu'on a testé, mais jamais en vrai avec un truc vraiment utile

  1. on peut patcher la mémoire et écrire ce qu'on veut, où on veut (code, données)
  2. on a plusieurs "fonctions" périodiques qui ne font rien, et qui sont prévues pour être patchée si on veut ajouter quelque chose (via la fonction de patch plus haut)

Donc on peut créer une fonction comme ça (en gros)

void generer_bit_flip(int address, int valeur) {
    static int actif = TRUE;

    if (actif) {
        *address = valeur;                                  // écrit la valeur correcte avec l'edac normal
        manipulate_specific_register_to_disable_edac();    // ou a du écrire la fonction, c'est pas aussi simple
        *address = valeur ^ 0x00000001;                     // écrit la valeur avec un bit changé, mais sans changer le checksum enregistré
        manipulate_specific_register_to_enable_edac();
        actif = FALSE;                                      // on ne veut le faire qu'une fois
    }
}

Une fois qu'on a la fonction, on la compile. Ensuite on charge le logiciel normal sur le banc, on se met en conditions "avant l'anomalie", on uploade la fonction, on l'active et …

Le banc change de mode, passe du mode 2, au mode 3, sur le calculateur redondant. On vérifie le contexte, même signature que l'anomalie en vol. C'est bon on a fini. (Ouf, mon journal est déjà trop long)

La correction (Over-The-Air, mais sans l'air)

Oui, non, pas exactement. On a une explication, il faut une correction maintenant. Bon, c'est simple. Pour lire une adresse alignée sur 4, il suffit de mettre deux bits à 0. Du coup voilà le patch

addresse = addresse & ~0x3      ; ** Cette ligne est le patch **
disable_edac_trap:              ;
load [adresse], reg             ;
enable_edac_trap:               ; 
store reg, [adresse]            ;

Oui, c'est un patch d'une instruction dans le binaire. (Techniquement, 5 instructions, parce qu'il faut décaler les 4 instructions existantes de 1, mais on avait des noop en dessous, du coup ça rentre)

La dernière question, c'est quelle stratégie d'update appliquer. On a techniquement 4 familles de satellites à considérer :

  1. les satellites "pré-existants", qui utilisent l'ancien compilateur, sans packed et déjà dans l'espace.
  2. le satellite qui a eu l'anomalie.
  3. les 5 autres satellites de la grappe.
  4. les futurs satellites, non lancés.

Ce qui a été décidé : La première catégorie : Techniquement, on pourrait discuter du fait qu'il y a un bug ou non. Mais même si on considère qu'il y a un bug, il ne peut pas être déclenché. Donc on ne touche à rien. La catégorie 4, c'est facile. Ils sont au sol, on fait une nouvelle version complète du logicielle, on reflashe la rom en entier, et on vérifie.

Il reste les deux autres catégories. Bon la seule différence, c'est qu'un tourne pour l'instant sur le calculateur redondant et est toujours en mode 3 (on peut revenir en mode 2, manuellement, si on veut). Donc on décide "on va faire la même chose", et on va corriger le problème (on aurait pu ne rien faire et dire "bah, si ça arrive, on connait et on revient à chaque fois manuellement en mode 2")

Là encore, même si on corrige, on a plusieurs choix :

  1. mettre à jour la ROM. En fait non, les ROM, parce que chaque calculateur a la sienne. Et le nominal ne peut pas écrire la ROM du redondant, et inversement. (du coup, si on veut patcher, qu'est-ce qu'on patche ? Le deux ROM ? Du coup il faut reconfigurer à la main pour rebooter sur le redondant ?)
  2. utiliser un mécanisme prévu pour "patcher, mais sans patcher la ROM".

La solution 2, retenue, c'est un mécanisme (déjà dans le logiciel) qui permet de mettre infos dans une autre mémoire (partagée par les deux calculateurs). Au boot, la ROM est copiée dans la RAM (on exécute le code depuis la RAM), et "avant de démarrer" on vient regarder dans cette table, si on doit patcher la RAM. Du coup on a quelque chose comme :

ROM (logiciel original) --> Copie vers la RAM --> RAM (logiciel original) --> fonction de patch au boot, vient modifier la RAM --> RAM (trap corrigée) --> boot du logiciel.

Conclusion

Qu'est-ce que je retiens principalement ?

  • quand on me dit que du code fonctionne, donc qu'il est correct… j'ai un doute
  • Ce n'est pas parce que la doc explique quelque chose qu'on peut le trouver. Surtout quand elle fait 5000 pages… Il ne faut pas hésiter à demander

Voila, en quelque pages, une vieille histoire qui m'a marqué. Je suis probablement une des personnes qui a participé à un des patchs le plus haut du monde (plus de 1000 km d'altitude)

Bon en vrai, la NASA fait des updates logicielles sur des rovers sur Mars, donc c'est clairement pas le record mais c'est pas trop mal (ils ont même peut être des updates sur leurs sondes plus loin de la terre)

Note: Cette histoire date maintenant d'il y plus de 10 ans. Il y a donc forcément des simplifications, des imprécisions, et probablement des erreurs. Aucun satellite n'a été blessé pendant cette enquête. Il y en a bien un qui est tombé à terre, mais ça c'était avant le lancement.

  • # Patch le plus lointain

    Posté par  (site web personnel) . Évalué à 10 (+13/-0).

    La NASA a patché Voyager 1 qui est presque à une journée-lumière de nous ( https://science.nasa.gov/mission/voyager/where-are-voyager-1-and-voyager-2-now/ ) pour rallumer ses moteurs ( https://www.jpl.nasa.gov/news/nasas-voyager-team-focuses-on-software-patch-thrusters/ )

    Ça fait un poil plus "haut" que 1000 km.

    \_o<

  • # une bien belle histoire

    Posté par  (site web personnel) . Évalué à 10 (+15/-0).

    quand un satellite bugue en prod (on va dire en vol, plutôt),

    Tu veux dire en chute libre ?

    Merci d'avoir pris le temps de rédiger cet excellent journal que je ne trouve d'une longueur tout à fait correcte.

    Ça m'a un peu rappelé le temps où j'étais payé pour comprendre des messages de kernel panic et des vmcores. Mais ça restait bien plus terre à terre.

    pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

    • [^] # Re: une bien belle histoire

      Posté par  (Mastodon) . Évalué à 10 (+10/-0).

      Tu veux dire en chute libre ?

      Disons qu'il tombe avec panache

      En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

  • # Classification des SEU

    Posté par  (site web personnel) . Évalué à 10 (+26/-1).

    Suite à ce genre de mésaventure, il a été décidé de classer les SEU en 2 catégories:
    - La catégorie L pour ceux qui sont biens gérés.
    - La catégorie M pour ceux qui engendrent des effets indésirés.

    Ainsi l'entreprise en charge de l'EDAC avait le SEU L qui ne posait pas de problème alors que vous, vous aviez le SEU M.

    Désolé /o\

    L'association LinuxFr ne saurait être tenue responsable des propos légalement repréhensibles ou faisant allusion à l'évêque de Rome, au chef de l'Église catholique romaine ou au chef temporel de l'État du Vatican et se trouvant dans ce commentaire

  • # OTA dans l'espace

    Posté par  (Mastodon) . Évalué à 10 (+8/-0). Dernière modification le 04 décembre 2025 à 09:50.

    Merci pour ce super journal !!!

    J'ai bossé et je bosse encore sur de l'OTA embarqué (j'ai fait téléphone Android, lampadaires - oui - et maintenant c'est sur des bagnoles). Avec le temps j'ai vu prendre (et j'ai pris) assez de claques pour ne plus trop me faire avoir, mais clairement, l'OTA c'est un sujet compliqué où les pièges à con affluent…

    Pour une bagnole, "au pire" tu fais un rappel de quelques milliers/millions de véhicules (gloups), mais ça reste techniquement possible. Pour un satellite… non faut pas se vautrer, mais vraiment pas.

    Tu aurais quelques règles à me donner que vous vous imposiez ? Par exemple j'ai vite appris que lors de mes tests la séquence "test version A / update vers B / test version B" n'était pas du tout suffisante ! En effet, passer sur une version B dont le système de mise à jour est buggué… ça piquera plus tard !

    Bref, dans ton journal j'ai vu passer des trucs comme "on a des boucles qui servent à rien sauf éventuellement à être patchées plus tard" (des hooks quoi). Même si je ne sais pas si c'est utilisable chez nous (on a des OTA de luxe, on change même les bootloaders !) je garde cette idée en tête, ça ressortira peut-être un jour sous une forme ou une autre.

    En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

    • [^] # Re: OTA dans l'espace

      Posté par  (site web personnel) . Évalué à 10 (+8/-0).

      Sur les sat, j ai vu plusieurs ROM de suite. Genre la ROM gold qui ne change jamais est le dernier fallback, qui a surtout la methode d'update de bien testé. Il peut même y avoir encore un fallback qui ne gère que la mise a jour.

      Donc, si tu as la place pour 2 codes, l'un est complètement read only. Ainsi quel que soit l'erreur, tu retournes dans un mode qui marche assez bien pour retenter le patch.

      Dans les toutes dernières versions, c'est plus des applications a charger et non tous le soft a changer. Le dolphin fait ça sur du stm32 mais je n ai pas vu comment.

      "La première sécurité est la liberté"

    • [^] # Re: OTA dans l'espace

      Posté par  (Mastodon) . Évalué à 10 (+26/-0).

      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 :

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

      .free_function_1:
          noop
          noop
          noop
          noop
          noop
      .free_function_end:
          noop

      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 :

      1. é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
      2. vérifier qu'on a bien écrit correctement ce qu'on voulait à l'adresse 0x42 pour être sûr en dumpant la mémoire
      3. changer (patcher) un des noop au début de la free_function pour faire un jump 0x42 (vérifier qu'on a bien écrit)
      4. 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.

      1. on écrit la version corrigée dans une zone libre
      2. on vérifie
      3. 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

      1. on ne veut pas mettre la ROM en mode écriture possible (radiations, erreurs, … et ce serait permanent) ; et
      2. ç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:

      1. la ROM est copiée en RAM
      2. quelque chose fait démarrer le logiciel à l'adresse 0xB00T (oui, T n'est pas un chiffre hexa… :-p)
      3. 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 :

      1. le code que je mettais à l'adresse 0x42
      2. le jump dans le code de la free function
      3. 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 :

      1. Non, la table de patch ne peut pas faire ça, on a pas assez de place.
      2. 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.

      1. logiciel original : 1.2 Mo
      2. logiciel avec "juste de quoi patcher et survivre dans l'espace" : moins de 2Mo - 1.2 Mo
      3. 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  (Mastodon) . Évalué à 4 (+1/-0).

        Merci infiniment, je vais décortiquer tout ça. Encore une fois, les concepts ne vont pas être applicables tels quels évidemment, mais l'idée sous-jacente fait toujours sens (enfin, c'est ce que je pense).

        En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

  • # Système d'exploitation ?

    Posté par  . Évalué à 7 (+6/-0).

    Salut
    Quel est l'OS qui fait tourner le satellite ?

    • [^] # Re: Système d'exploitation ?

      Posté par  (Mastodon) . Évalué à 10 (+8/-0).

      C'était un OS développé en interne. Je pense que c'était le cas sur tous les satellites sur lesquels j'ai travaillé (plutôt dans l'industrie spatiale classique, des satellites bien chers, de plusieurs centaines de kilo à quelque tonnes. Dans le "new space", j'imagine qu'ils ont des choses un peu plus "cots" pour l'OS, mais je ne suis pas sûr)

      Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

    • [^] # Re: Système d'exploitation ?

      Posté par  (Mastodon) . Évalué à 9 (+6/-0). Dernière modification le 04 décembre 2025 à 09:55.

      Windows CE je crois ^^

      Plus sérieusement, on va attendre la réponse mais je miserais quelques francs sur "pas d'OS, que du bare metal !".

      EDIT: ah bin non, OS quand même ;)

      En théorie, la théorie et la pratique c'est pareil. En pratique c'est pas vrai.

      • [^] # Re: Système d'exploitation ?

        Posté par  (site web personnel, Mastodon) . Évalué à 9 (+8/-1).

        OS développé en interne == bare-métal amélioré

        Je veux dire, ce qu'il appelle "OS interne", je pense que ce sont des libs de gestion de certaines procédures que l'on colle au soft pour faire un bare-metal intelligent. Mais je doute qu'il ai un système de fichier, une gestion fine et configurable du multi-coeur, un shell complexe, une gestion de chargement de nouveaux programme…
        C'est plus : un soft que tu flash sur toute la ROM. Tu veux mettre un patch, tu reflash toute la ROM.

        PS: Sinon, l'OS serait vendu pour être rentabilisé. Mais il devait être trop spécifique à un matos, certains périphériques…

        Sous licence Creative common. Lisez, copiez, modifiez faites en ce que vous voulez.

        • [^] # Re: Système d'exploitation ?

          Posté par  (Mastodon) . Évalué à 10 (+11/-0).

          Mais je doute qu'il ai un système de fichier, une gestion fine et configurable du multi-coeur, un shell complexe, une gestion de chargement de nouveaux programme

          Exact. On n'a pas de système de fichier, et ce calculateur là n'est pas multi coeur, et c'est un monolithe (on n'a pas d'exécutable qu'on peut lancer)

          La définition d'OS, c'est plutôt "vague", mais ici l'OS était responsable de

          1. le scheduling des tâches. Y compris le passage de l'une à l'autre, … (c'est du multitâche, mais la fréquence et priorité des tâches était fixe)
          2. la vérification du temps réel (watchdog, les taches sont bien démarrées et terminées à temps)
          3. tout ce qui est initialisation des périphériques
          4. je rentre aussi dans "OS" tout ce qui est gestion des traps et leur codes

          Mais je ne peux pas uploader simplement un nouveau exécutable et lui dire "rajoute une tâche et lance cet exe".

          Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

    • [^] # Re: Système d'exploitation ?

      Posté par  (site web personnel, Mastodon) . Évalué à 5 (+3/-0).

      J'ai trouvé Parsimoni mais je ne suis pas certains que ce soit toujours de véritables OS (Du moins historiquement, aujourd'hui peut-être plus). Je pense que du bare-metal ferait sens au moins sur certains petits satellites.

      Sous licence Creative common. Lisez, copiez, modifiez faites en ce que vous voulez.

    • [^] # Re: Système d'exploitation ?

      Posté par  (Mastodon) . Évalué à 6 (+4/-0).

      Quel est l'OS qui fait tourner le satellite ?

      C'est un système, pas un système d'exploitation. Un peu comme la calculatrice Casio College FX92, qu'on connait tous : le système est pas là pour faire "ce que tu veux", mais ce pourquoi le concepteur/fabriquant l'a programmé. C'est aussi simple que ça.

  • # Next level

    Posté par  . Évalué à 6 (+6/-0).

    Excellent article ! Je n'ai pas encore été touché par des erreurs d'alignement, mais on m'y a sensibilisé dans une revue de code il y a longtemps déjà.

    C'est le genre d'erreur qui semble difficile à rencontrer jusqu'au jour où…

    • Les architectures les plus répandues, avec lesquelles on apprend à programmer, gèrent généralement les accès non alignés.
    • Il faut du code qui manipule des pointers (offsets non triviaux, casts).
    • Pas tellement documenté dans les tutoriaux.

    Donc on y est réellement exposé un peu tard dans son parcours.

    Vous faites comment pour empêcher ce genre de bombe à retardement d'arriver dans votre code ?

    • [^] # Re: Next level

      Posté par  (Mastodon) . Évalué à 8 (+6/-0).

      En vrai je n'ai jamais eu de problème d'alignement "dans mon code", c'était vraiment une combinaison de facteur, et une "fonction" (?) écrite en assembleur qui appelait une instruction "incorrecte" par rapport aux arguments passés.

      Dans le code écrit avec un language "normal", le compilateur devrait gérer ça pour moi (après, techniquement, il faut faire attention à la perfo, lire des choses non alignée peut être plus lent, sur certains architecture. Ca devient un compromis taille/vitesse)

      Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

      • [^] # Re: Next level

        Posté par  . Évalué à 3 (+3/-0).

        D’accord avec toi sur les deux points. Mon propos était davantage que c’est trop facile de faire des conversions et de l’arithmétique de pointeurs qui résultent en des accès involontairement non-alignés non-détectés/gérés par le compilateur, qui peuvent ainsi :
        1. nuire à la perf
        2. planter directement (architecture non testée)
        3. planter “indirectement” comme dans ton cas

        Du coup ma question reformulée est : comment peut-on détecter les problèmes d’alignement non détectés par le compilateur, dans le cas où au pire les machines de test sont juste (un peu) ralenties ? Je préférais avoir un plantage (cf. cas 2) ou mieux : un analyseur qui détecte ça (je ne crois pas que clang-tidy le fasse par exemple).

        • [^] # Re: Next level

          Posté par  (site web personnel) . Évalué à 4 (+2/-0).

          comment peut-on détecter les problèmes d’alignement non détectés par le compilateur

          Mauvais compilateur, changer compilateur. clang a __builtin_is_aligned, C23 a memalignment et alignof, la proposition qui est entrée dans C23 a d'autres exemples dans la section "Prior Art". Si tu as des exemples spécifiques que ton compilateur ne détecte pas, je pense que tu peux en faire des rapports de bug.

          pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

          • [^] # Re: Next level

            Posté par  . Évalué à 3 (+3/-0). Dernière modification le 04 décembre 2025 à 21:36.

            Je connais les fonctions, je demande comment détecter (pas corriger) les problèmes d'alignement sur une base de code existante, ou automatiquement pour éviter tout nouveau code qui utilise des accès non alignés.

  • # c'est quoi comme matériel?

    Posté par  . Évalué à 5 (+4/-0).

    Salut, j'ai lu l'article avec intérêt. C'était très bien. Une question que je me pose: c'est quoi comme matériel? 4 Mo de RAM en cache? ou puce mémoire dédiées? c'est quoi comme processeur? c'est quoi le jeu d'instruction? S'agit-il de microcontrôleur? 2 Mo de ROM, EEPROM, embarqués?

    Il me semble avoir lu qu'Atmel avait des processeurs ou microcontrôleurs rad-hard (résistants aux radiations), fabriqués à Rousset en Provence, avant l'acquisition par Microchip. J'ai peut-être tout faux.

    • [^] # Re: c'est quoi comme matériel?

      Posté par  . Évalué à 5 (+3/-0).

      SPARC + Spacial, ce pourrait être du LEON ? Mais, il me semble que la correction d'erreur n'est dispo qu'en LEON 3, qui sauf erreur de ma part n'existe qu'en softcore…

      • [^] # Re: c'est quoi comme matériel?

        Posté par  (Mastodon) . Évalué à 1 (+0/-1).

        SPARC

        SParc, je connais pas trop

        je lisais sur wiki que les powerpc, très appréciés des Mac des années 90, fournissaient pas mal d'embarqués, jusqu'aux satellites starlink.. Alors qu'avoir un mac et son "firefox" sur powerpc, fait très ringard… ;)

    • [^] # Re: c'est quoi comme matériel?

      Posté par  (Mastodon) . Évalué à 6 (+4/-0).

      A priori toutes les mémoires étaient des puces dédiées. Le processeur, c'était un ERC32, SPARC v7 pour le jeu d'instruction. C'était avant les LEON, pour répondre à aiolos dans l'autre commentaire. Et c'était Atmel qui le fabriquait, mais je ne sais pas où il était fabriqué.

      Tous les nombres premiers sont impairs, sauf un. Tous les nombres premiers sont impairs, sauf deux.

  • # ======

    Posté par  (Mastodon) . Évalué à 5 (+5/-2).

    "He dropped the bomb"

    ça change des publications politiques et autres petites publicités, faut admettre : à ce niveau d'ingénierie, on se sent rapidement tout petit à coté..

    • [^] # Re: ======

      Posté par  (site web personnel) . Évalué à 4 (+5/-3).

      Cela dit je vois pas le rapport avec Linux ou le Logiciel Libre.

      /s

      pertinent adj. Approprié : qui se rapporte exactement à ce dont il est question.

      • [^] # Re: ======

        Posté par  (site web personnel) . Évalué à 7 (+6/-0).

        Par contre niveau Culture c'est plutôt pas mal ;-)

        Et des histoires d'alignement, on doit pouvoir tomber dessus si on travaille sur du logiciel libre pour des micro-contrôleurs, donc c'est bien de se rappeler que ça peut arriver.

  • # On a eu chaud

    Posté par  (site web personnel) . Évalué à 7 (+5/-0).

    Un peu plus, et on avait un satellite qui s'écrasait sur Paris.

    https://www.ina.fr/ina-eclaire-actu/paco-rabanne-station-spatiale-mir-eclipse-31-aout-1999-chute-paris-prophetie-nostradamus-apocalypse

    Noël aux tisanes, pâques aux rabanes

  • # Rayon cosmique lors des élections par vote électronique à Schaerbeek

    Posté par  (site web personnel) . Évalué à 6 (+4/-0).

  • # vous joindre directement?

    Posté par  (Mastodon) . Évalué à 2 (+0/-0).

    message pour l'auteur : vous pouvez pas nous laisser comme ça ;)

    j'entends : l'article est super intéressant, mais j'imagine, que beaucoup aimeraient vous poser des questions, à ce sujet, sans forcément voir les échanges de manière complètement publics..

    auriez vous une coordonnée numérique, un compte sur un réseau, pour vous joindre par MP?

    le post est si intéressant : mathématiques, algo, satellites, espaces, électronique, embarqué, que bcp, aujourd'hui ou demain, risquent de vouloir poser bcp de questions, voire élargir aux sujets satellitaires et espace.. y aurait il moyen de vous contacter directement, sans passer par ces colonnes?

    merci :)

  • # Coquilles

    Posté par  . Évalué à 2 (+0/-0).

    Merci pour l'article de haut vol, j'ai appris plein de choses ! ;-)

    J'ai repéré deux coquilles:
    s/Du coup, là, c'est là ou David revient/Du coup, là, c'est là où David revient/ (ou -> où)
    s/c'est qu'un tourne pour l'instant sur le calculateur redondant est est toujours en mode 3
    /c'est qu'un tourne pour l'instant sur le calculateur redondant et est toujours en mode 3
    / (est -> et)

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.