
Objet	Re: déverrouillage matériel
De	parisse
À	Yann COUTURIER
Cc	Xavier Andréani
Date	2025-11-09 17:56
Salut Yann,

J'ai recreusé la question de la calibration. Sur les Epsilon 24, il y a 7 données de calibration, sur Epsilon 23.2.6, il y a 5 données de calibration, sur la 19.5.9 pour n0115 il y en avait 5 (contre 4 sur Epsilon 20 pour n120). Ce sont les mêmes données de calibration pour toutes les calculatrices, ça doit dépendre des évolutions du controleur LCD utilisé par Numworks.
Conséquence: le downgrade en version 19.5.9 (N115 ou N110) ou 20.2.0 (N120) n'est probablement pas compatible pour les LCD des dernières calculatrices vendues (celles vendues en 2025? faudrait mettre la main sur un kernel d'Epsilon d'une n120 neuve). Attendre mai prochain pour utiliser les failles n'a plus d'intérêt, il suffit d'attendre la publication des sources d'Epsilon 24.
De plus pour les calculatrices récentes, il ne restera que le déverrouillage matériel pour la compatibilité KhiCAS, et c'est probablement indispensable pour Upsilon, d'où l'intérêt de creuser.

Et comme indiqué précédemment, je pense que c'est possible en suivant les étapes suivantes
1/ reprogrammation hardware de la flash interne pour a/ redémarrer sur le bootloader ST en BOOT0 et BOOT1, b/ RDP1 vers RDP0 et mass erase de la flash
2/ redémarrer la calculatrice, donc sur le bootloader de ST, puis soit envoyer le bootloader d'Upsilon après modification de ion/src/device/shared/drivers/display.cpp pour y ajouter les données de calibration dans initPanel(), le reste d'Upsilon devrait marcher sur les n0115(?), soit envoyer le bootloader de Numworks en désactivant la signature et en RAM le flasher d'Epsilon 15.5 (rescue mode) modifié pour juste reprogrammer les options bytes de la flash interne pour pouvoir rebooter sur la flash interne.
Je devrais pouvoir faire des tests du dernier point sur une N0100 que je croyais morte mais qui en fait avait seulement la batterie complètement déchargée, par exemple en reprogrammant les options bytes de la flash interne pour inverser les boots.
Est-ce que tu penses que ça vaut le coup, souhaites-tu participer (ou d'autres de la communauté de dev Numworks), si oui en faisant quoi ?

Ci-dessous l'extrait de décompilation avec hexedit et Ghidra sur un kernel Epsilon 24.
Il y a un gros morceau de code qui recopie les données de calibration de la flash vers la RAM, probablement pour être sur de ne pas avoir de délai d'accès lors de l'envoi des données au driver LCD. Ensuite, un test du panelId() pour savoir quelles données de calibration il faut envoyer.

à+

En 24.5, il y a 7 jeux de données de calibration qui commencent en 0x539c, chacun contient 14 octets suivi d'un 0
00005390   32 41 4C 57  50 2D 4E 30  31 32 30 00  A2 0A 11 0A  2ALWP-N0120.....
000053A0   0C 1A 34 22  4D 28 15 13  29 2D 00 F0  09 0F 08 07  ..4"M(..)-......
000053B0   03 28 11 3C  17 12 11 28  2D 00 F0 0D  12 09 08 14  .(.<...(-.......
000053C0   2D 22 43 38  17 19 2C 30  00 F0 09 0D  0A 09 05 29  -"C8..,0.......)
000053D0   43 40 39 18  16 2E 2E 00  C4 0C 13 0C  0E 3C 36 44  C@9..........<6D
000053E0   4F 3A 17 15  2B 2F 00 F0  02 08 06 07  06 31 43 48  O:..+/.......1CH
000053F0   36 12 12 2B  34 00 F0 08  0C 0B 09 23  31 33 47 37  6..+4......#13G7
00005400   14 14 2C 32  00 1F 00 01  00 18 50 00  00 00 00 00  ..,2......P.....

void suspend_restart(int param_1){ // 0x90002ed4
  byte bVar1;
  uint *puVar2;
  undefined2 *puVar3;
  undefined2 *puVar4;
  uint *puVar5;
  uint uVar6;
  ushort *puVar7;
  int extraout_r1;
  uint extraout_r1_00;
  int extraout_r1_01;
  int extraout_r1_02;
  undefined4 uVar8;
  int extraout_r1_03;
  uint extraout_r1_04;
  uint extraout_r1_05;
  uint extraout_r1_06;
  uint extraout_r1_07;
  byte *pbVar9;
  uint uVar10;
  undefined2 uVar11;
  undefined4 *puVar12;
  undefined4 *puVar13;
  int iVar14;
  byte *pbVar15;
  byte *pbVar16;
  byte *pbVar17;
  undefined2 uVar18;
  byte *pbVar19;
  int iVar20;
  undefined4 local_9c;
  undefined1 local_98;
  byte local_94 [4];
  undefined4 auStack_90 [2];
  undefined2 local_88 [2];
  byte local_84 [4];
  undefined4 auStack_80 [2];
  undefined2 local_78 [2];
  byte local_74 [4];
  undefined4 auStack_70 [2];
  undefined2 local_68 [2];
  byte local_64 [4];
  undefined4 auStack_60 [2];
  undefined2 local_58 [2];
  byte local_54 [4];
  undefined4 auStack_50 [2];
  undefined2 local_48 [2];
  byte local_44 [4];
  undefined4 auStack_40 [2];
  undefined2 local_38 [2];
  byte local_34 [4];
  undefined4 auStack_30 [2];
  undefined2 local_28 [2];
  int local_24;
  ...
  // LCD
  _DAT_60000000 = 1;
  msleep_svc(5);
  _DAT_60000000 = 0x11;
  msleep_svc(5);
  // calibration data copie de la flash vers la stack en RAM (vitesse?)
  puVar4 = DAT_9000355c;
  puVar3 = DAT_900032c0;
  pbVar15 = local_94;
  puVar12 = (undefined4 *)PTR_DAT_900032a4; // 9000539c
  do {
    puVar13 = puVar12;
    pbVar16 = pbVar15;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar16 = *puVar13;
    *(undefined4 *)(pbVar16 + 4) = uVar8;
    pbVar15 = pbVar16 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032a4 + 8));
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar16 + 8) = *puVar12;
  *(undefined2 *)(pbVar16 + 0xc) = uVar18;
  pbVar15 = local_84;
  puVar12 = (undefined4 *)PTR_DAT_900032a8; // 53ab
  do {
    puVar13 = puVar12;
    pbVar16 = pbVar15;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar16 = *puVar13;
    *(undefined4 *)(pbVar16 + 4) = uVar8;
    pbVar15 = pbVar16 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032a8 + 8));
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar16 + 8) = *puVar12;
  *(undefined2 *)(pbVar16 + 0xc) = uVar18;
  pbVar15 = local_74;
  puVar12 = (undefined4 *)PTR_DAT_900032ac; // 53ba
  do {
    puVar13 = puVar12;
    pbVar16 = pbVar15;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar16 = *puVar13;
    *(undefined4 *)(pbVar16 + 4) = uVar8;
    pbVar15 = pbVar16 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032ac + 8));
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar16 + 8) = *puVar12;
  *(undefined2 *)(pbVar16 + 0xc) = uVar18;
  pbVar15 = local_64;
  puVar12 = (undefined4 *)PTR_DAT_900032b0; // 53c9
  do {
    puVar13 = puVar12;
    pbVar16 = pbVar15;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar16 = *puVar13;
    *(undefined4 *)(pbVar16 + 4) = uVar8;
    pbVar15 = pbVar16 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032b0 + 8));
  pbVar15 = local_54;
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar16 + 8) = *puVar12;
  *(undefined2 *)(pbVar16 + 0xc) = uVar18;
  pbVar16 = pbVar15;
  puVar12 = (undefined4 *)PTR_DAT_900032b4; // 53d8
  do {
    puVar13 = puVar12;
    pbVar17 = pbVar16;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar17 = *puVar13;
    *(undefined4 *)(pbVar17 + 4) = uVar8;
    pbVar16 = pbVar17 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032b4 + 8));
  pbVar16 = local_44;
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar17 + 8) = *puVar12;
  *(undefined2 *)(pbVar17 + 0xc) = uVar18;
  pbVar17 = pbVar16;
  puVar12 = (undefined4 *)PTR_DAT_900032b8;
  do {
    puVar13 = puVar12;
    pbVar9 = pbVar17;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar9 = *puVar13;
    *(undefined4 *)(pbVar9 + 4) = uVar8;
    pbVar17 = pbVar9 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032b8 + 8));
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar9 + 8) = *puVar12;
  *(undefined2 *)(pbVar9 + 0xc) = uVar18;
  pbVar17 = local_34;
  puVar12 = (undefined4 *)PTR_DAT_900032bc;
  pbVar9 = pbVar17;
  do {
    pbVar19 = pbVar9;
    puVar13 = puVar12;
    puVar12 = puVar13 + 2;
    uVar8 = puVar13[1];
    *(undefined4 *)pbVar19 = *puVar13;
    *(undefined4 *)(pbVar19 + 4) = uVar8;
    pbVar9 = pbVar19 + 8;
  } while (puVar12 != (undefined4 *)(PTR_DAT_900032bc + 8));
  uVar18 = *(undefined2 *)(puVar13 + 3);
  *(undefined4 *)(pbVar19 + 8) = *puVar12;
  *(undefined2 *)(pbVar19 + 0xc) = uVar18;
  // fin de copie des calibrations
  // panelId()
  // liste de commandes connues sur 4e4101 et 4e4801
  // 0x21 displayinversionon
  // 0x29 displayon
  // 0x35 tearingeffectlineon,
  // 0x3A pixelformatset,
  // 0xc6 frameratecontrol
  // e0 +voltagegammacalibration, e1 negative
  // attention le pseudo-listing C est incomplet
  uVar6 = (uint)(byte)*DAT_900032c0 << 8 | (uint)(byte)*DAT_900032c0 << 0x10 |
          (uint)(byte)*DAT_900032c0;
  if (uVar6 == DAT_900032cc) { // 4e4601
    *DAT_900032c0 = 0x55; // 55 ?, -> 60020000
    *puVar3 = 0;
    _DAT_60000000 = 0xc6; // framerate
    *puVar3 = 5;
    pbVar17 = pbVar15;
LAB_9000326a:
   send_long_command(0xe0,0xe,pbVar15);
   send_long_command(0xe1,0xe,pbVar17);
  }
  else {
    if ((int)uVar6 <= (int)DAT_900032cc) { // < 4e4601
      if (uVar6 == DAT_900032c4) { // 4e4102
        *DAT_900032c0 = 0x55;
        _DAT_60000000 = 0x35;
        *puVar3 = 0;
        pbVar16 = local_84;
        pbVar17 = local_84;
LAB_9000354e:
        uVar18 = 5;
        iVar20 = 1;
LAB_900035b6:
        local_9c = *DAT_9000367c; // 0c 0c 00 33 33
        local_98 = *(undefined1 *)(DAT_9000367c + 1);
       send_long_command(0xb2,5,(byte *)&local_9c);
        puVar3 = DAT_90003680;  // 60020000
        *DAT_90003680 = 0x2c;
        *puVar3 = 1;
        _DAT_60000000 = 0xc4;
        *puVar3 = 0x20;
        local_9c = CONCAT22(local_9c._2_2_,0xa1a4);
        send_long_command(0xd0,2,(byte *)&local_9c); // a1 a4
        if (iVar20 == 1) {
          *puVar3 = 0x35;
          *puVar3 = 0x3c;
          uVar11 = 0x10;
        }
        else {
          *puVar3 = 0x57;
          *puVar3 = 0x1c;
          uVar11 = 0xf;
        }
        *puVar3 = uVar11;
        _DAT_60000000 = 0xc6;
        pbVar15 = pbVar16;
        _DAT_60020000 = uVar18;
      }
      else {
        if ((int)uVar6 <= (int)DAT_900032c4) { // < 4e4102
          uVar10 = DAT_900032c4 - 1; // 4e4101
          goto LAB_90003244;
        }
        if (1 < DAT_90003558 + uVar6) // 0xFFB1BEFB+uVar6
       goto LAB_900032d8;
LAB_90003248:
        *DAT_900032c0 = 0x55; // 60020000
        *puVar3 = 0;
        _DAT_60000000 = 0xc6;
        *puVar3 = 0x1e;
        pbVar15 = local_94;
        pbVar17 = local_94;
      }
      goto LAB_9000326a; // positive and negative gamma calibration
    }
    if (DAT_9000358c < (int)uVar6) { // 4e4902
      if (uVar6 == DAT_90003598) { // 4e5401
        *DAT_900032c0 = 0x55; // 60020000
        _DAT_60000000 = 0x35;
        *puVar3 = 0;
        pbVar16 = local_74;
        pbVar17 = local_64;
        goto LAB_9000354e;
      }
    }
    else {
      uVar10 = DAT_90003594; // 4e4801
      if (DAT_90003590 < (int)uVar6) { // 4e4900
        *DAT_900032c0 = 0x55; // 60020000
        uVar18 = 0xf;
        iVar20 = 2;
        _DAT_60000000 = 0x35;
        *puVar3 = 0;
        goto LAB_900035b6;
      }
LAB_90003244:
      if (uVar6 == uVar10) goto LAB_90003248;
    }
LAB_900032d8:
    *DAT_9000355c = 0x55; // 60020000
    *puVar4 = 0;
    *puVar4 = 5;
  }
  pbVar15 = DAT_90003560;
  _DAT_60000000 = 0x29; // displayon
  // end calibration
...
}

panelid:
4e4601 cmd data
  cmd 3a data 55
  cmd 35 data 0
  c6 5

4e4142
  3a 55
  35 0
  

4e4101 4e4801
  3a 55
  35 0
  c6 1e

4e5401
  3a 55
  35 0