2 points par GN⁺ 2024-06-09 | 1 commentaires | Partager sur WhatsApp
  • La multiplication en virgule flottante du VU de la PS2 comporte une erreur d’opération d’1 bit, si bien que pour certaines valeurs, 1 * X peut différer de X
  • D’après le manuel développeur du VU, X * 1 est garanti exact, mais 1 * X ne bénéficie pas de la même garantie, et cette différence devient un signal de détection d’émulateur
  • L’exemple utilise 129.5f, choisi parmi des valeurs problématiques trouvées par force brute, pour vérifier la différence de comportement entre une vraie PS2 et un émulateur
  • L’implémentation, en mode macro VU0, consiste simplement à multiplier 129.5f par 1, puis à vérifier si le résultat diffère de l’entrée d’origine
  • PCSX2, Play!, DobieStation et hps2x64 n’émulent actuellement pas ce comportement, et la difficulté de détection est évaluée à 1/5

Erreur d’1 bit dans la multiplication VU de la PS2

  • Cette méthode est le deuxième volet d’une série sur la détection d’émulateur PS2, et peut être utilisée avec VU1, le mode micro de VU0 et le mode macro de VU0
  • L’exemple utilise le mode macro de VU0 pour simplifier l’implémentation
    • VU0 est utilisé comme un coprocesseur, donc l’exécution peut se faire directement depuis le CPU EE
    • Il n’est pas nécessaire de manipuler un programme VU séparé
  • Le manuel développeur du VU indique dans les commentaires des instructions de multiplication comme MUL et MULi qu’il existe une erreur d’opération d’1 bit
    • 1 * X peut différer de la valeur d’origine X
    • Si VF[fs] est utilisé comme multiplicande, l’exactitude du résultat sous la forme X * 1 est garantie
  • La raison exacte de cette perte de bit n’est pas clairement établie

Valeur de détection et méthode d’implémentation

  • Pour détecter cette erreur, il faut un nombre qui la provoque, et la méthode de recherche la plus simple est la force brute
  • L’auteur a auparavant établi une liste des 250 premiers nombres, espacés de 0.5, qui provoquent le problème, et l’a publiée dans ce gist
  • Le code d’exemple utilise 129.5f comme valeur cible pour la détection
    • QMTC2 définit 129.5f dans VF1
    • VADDw crée 1 dans VF2
    • VMUL calcule VF1 = 1 * 129.5f
    • QMFC2 rapatrie le résultat côté EE pour le comparer à l’entrée
  • La valeur de retour est in[0] != out[0], et si la valeur d’origine diffère du résultat de la multiplication, on considère que l’erreur de multiplication VU est présente

Impact selon les émulateurs

  • Actuellement, PCSX2, Play!, DobieStation, hps2x64 n’émulent pas ce comportement de multiplication VU de la PS2
  • Comme il suffit de multiplier un nombre par 1 puis de vérifier le résultat, la difficulté de cette méthode de détection est estimée à 1/5

1 commentaires

 
GN⁺ 2024-06-09
Avis sur Hacker News
  • L’astuce la plus simple pour détecter une ancienne émulation ARM semble être la même que celle utilisée dans certaines protections anti-copie Game Boy Advance : stocker une instruction piège à la position PC+4, c’est-à-dire l’instruction juste après
    Sur un vrai ARM, à cause du pipeline, le processeur lit PC+8 pendant qu’il exécute PC et décode PC+4, donc l’instruction nouvellement écrite ne devrait pas avoir d’effet. Un émulateur qui ne reproduit pas le pipeline matériel finira par exécuter cette instruction
    Un article qui l’explique plus en détail, avec plusieurs techniques anti-émulation de 2004 : https://mgba.io//2014/12/28/classic-nes/
    • Le processeur de signal numérique TI320C40 de Texas Instruments avait des problèmes de pipeline encore plus bizarres : il y avait des slots de délai de branchement (https://en.wikipedia.org/wiki/Delay_slot), où une ou plusieurs instructions après un branchement s’exécutaient avant le branchement réel, ainsi que des slots de délai de chargement où une valeur stockée dans un registre n’apparaissait que plusieurs instructions plus tard
      Il semble que la valeur du registre soit restée indéfinie pendant quelques cycles. Écrire du code assembleur optimisé au cordeau pour ce genre de puce donnait une impression assez atroce, comme jouer à un clone de Zachtronics particulièrement pervers
    • Sur x86, la file de prélecture produisait un comportement similaire, mais Intel a décidé à partir du Pentium de détecter le code auto-modifiant, de sorte que modifier une instruction sur le point d’être exécutée finissait toujours par avoir un effet
      Bien plus tard, quelqu’un a trouvé un autre cas limite non détecté : une instruction de chaîne répétée qui s’écrase elle-même
      https://silviocesare.wordpress.com/2009/02/02/anti-debugging...
    • Certains CPU à pipeline conservent la compatibilité avec le code auto-modifiant : si l’on écrase une instruction déjà engagée dans le pipeline, ils le détectent et vident le pipeline
      x86 a ce type de mécanisme, mais je ne sais pas s’il a fini par être supprimé dans la variante 64 bits
    • Est-ce que c’est la raison pour laquelle la ROM de Dragon Ball Z: The Legacy of Goku II ne fonctionnait parfois pas dans VisualBoyAdvance ?
  • C’est précisément pour cela que l’émulation visant 100 % de précision relève de l’artisanat dans l’industrie
    Il faut non seulement connaître tous les comportements particuliers du matériel et du logiciel d’origine, mais aussi les reproduire tels quels, aussi étranges soient-ils. C’est déjà difficile en soi, et il faut encore tenir compte de l’impact sur les performances
    • Un émulateur doit rester réaliste sur la précision. Quand on émule des systèmes plus modernes, viser à la fois une précision matérielle de 100 % et des performances utilisables est généralement impossible ; on accepte donc des compromis techniquement différents du matériel réel, mais dont les écarts observables en pratique sont minimes
      Utiliser un recompileur JIT empêche d’être parfaitement exact au cycle près par rapport au matériel d’origine, mais ce n’est généralement pas un problème, sauf si le code du jeu a été conçu exprès pour casser les émulateurs
      Dolphin a lui aussi dû gérer cet équilibre quand quelques jeux Wii commerciaux ont inclus du code anti-émulateur exploitant des détails du comportement du cache du vrai CPU Wii. En théorie, il aurait été possible d’émuler le vrai cache CPU pour faire tourner le jeu correctement, mais le surcoût en performances aurait probablement ralenti le tout d’un facteur 10, rendant le jeu injouable ; ils ont donc choisi un patch de contournement
      https://dolphin-emu.org/blog/2017/02/01/dolphin-progress-rep...
  • Comment entre-t-on dans l’émulation ? Cela ressemble à un domaine de niche incroyablement difficile, même au sein du logiciel
    On dirait qu’il faut à la fois comprendre l’électronique et une forme de sorcellerie avancée en programmation
    • Comme pour tout le reste, il faut commencer progressivement
      Au début, et même dans la plupart des cas à la fin, il n’est pas nécessaire de comprendre une magie profonde. En général, il suffit de lire les spécifications et de les implémenter telles quelles. Il faut savoir structurer son code pour que cela ne devienne pas ingérable, mais il existe des schémas courants, et après avoir créé un ou deux émulateurs, cela devient beaucoup plus simple
      Il est aussi rarement nécessaire de comprendre l’électronique. Ce qu’on émule, c’est le comportement. Quand on découvre un bug dans le comportement du matériel d’origine, il suffit généralement d’ajouter un traitement spécial dans l’émulateur. Des connaissances en électronique peuvent aider à comprendre pourquoi ce comportement existe, mais c’est plus de l’intérêt historique qu’un besoin pratique
      Il existe des difficultés propres à ce domaine. Quand un problème survient, on se retrouve souvent à déboguer en même temps sa compréhension du matériel, l’implémentation de l’émulateur et le jeu en cours d’émulation. Il peut être difficile d’isoler la cause exacte. Cela dit, je recommande quand même de bricoler quelque chose rapidement. Ce n’est pas élégant, mais tous les émulateurs sont pleins de traitements spéciaux destinés à faire fonctionner tant bien que mal les jeux populaires. Si quelques hacks sales suffisent à faire tourner un jeu, alors il faut les faire. Il n’est pas nécessaire de reproduire exactement le comportement du matériel d’origine ; il faut simplement faire en sorte que le jeu fonctionne
    • On peut commencer par lire la documentation du matériel, sans avoir besoin de comprendre la machine au niveau des circuits électroniques. Ce n’est pas une simulation de circuits numériques, donc ce n’est pas nécessairement aussi complexe
      Un CPU 8 bits est une simple machine à états qui ne possède que quelques octets d’état, c’est-à-dire des registres. Il suffit de lire le programme octet par octet et d’imiter ce que le CPU ferait après avoir lu chacun de ces octets. Ce sont des opérations très simples, comme additionner ou soustraire des nombres, ou lire et écrire des octets
      http://www.6502.org/users/obelisk/6502/registers.html
      http://www.6502.org/users/obelisk/6502/instructions.html
      Un émulateur de CPU 6502 lit les quelques octets suivants du programme, interprète ces octets comme des instructions, puis exécute ces instructions. Au passage, il met à jour quelques registres ou compteurs du CPU, effectue des opérations arithmétiques ou bit à bit et, si nécessaire, lit ou écrit 1 octet de données d’un emplacement à un autre. Il répète ce processus dans une boucle infinie

Il s’agit d’une simulation du cycle extraction-décodage-exécution
https://en.wikipedia.org/wiki/Instruction_cycle

  • Cela dépend de ce qu’on entend par émulation
    Il y a longtemps, j’ai porté un interpréteur 6502 d’UNIX vers le Classic Macintosh pour lire des fichiers de musique SID. Il suffisait qu’il tourne assez vite, donc la précision au cycle d’horloge n’avait pas d’importance
    Il fonctionnait en appelant du code C depuis l’interpréteur
  • Quand j’ai essayé de m’y mettre il y a longtemps, le conseil était de commencer par quelque chose de très simple et bien documenté, puis de progresser à partir de là
    J’ai toujours envie d’essayer, mais je n’ai pas le temps
  • Petite autopromo : j’ai fait une présentation sur ce sujet au dernier FOSDEM : https://fosdem.org/2024/schedule/event/fosdem-2024-2146-how-...
    À part ça, le commentaire frère de @xcv123 dit vraiment juste
  • En émulation logicielle, c’est probablement le genre d’exemple intéressant qui ne vaut pas vraiment la peine qu’on s’en préoccupe. Émuler aussi ce bug rendrait probablement le tout assez lent
    Si un jour il devient possible de recréer la PS2 sur FPGA, comprendre comment ce comportement se produisait pourrait devenir un projet amusant pour quelqu’un
    • Les implémentations sur FPGA sont elles aussi souvent construites à partir du code ou de la documentation de projets d’émulation logicielle
      Rien ne garantit qu’une version FPGA de la PS2 n’implémenterait pas le même bug, ou un bug similaire
    • Tout dépend de savoir si ce bug casse le jeu ou non
    • Corriger ce bug ferait partie du travail de correction de divers autres bugs en virgule flottante, plus précisément des problèmes d’arrondi et de clamp
      La virgule flottante logicielle serait lente, mais la solution générale suivrait probablement l’émulateur PS2 de la PS4 : mettre sur liste blanche, jeu par jeu, les sections de code autorisées à utiliser un chemin de virgule flottante logicielle
    • Pourquoi vouloir émuler sur un FPGA relativement coûteux un vieux CPU MIPS médiocre ? L’intérêt central de l’émulation d’anciennes consoles, c’est de pouvoir jouer à de vieux jeux sur ordinateur ou téléphone, indépendamment du matériel d’origine
  • En voyant le titre, y a-t-il encore eu des gens à se demander pourquoi une souris ou un clavier devait faire des maths ?
    Il m’a fallu bien trop longtemps pour comprendre qu’il s’agissait de la PlayStation 2, et non du port Personal System/2 qui sert à connecter souris et clavier
    • L’un est PS2, l’autre PS/2
    • Pour ceux qui ont voté contre : ce n’est pas parce que c’est clair pour vous que ça l’est pour tout le monde
      Les sigles de trois lettres peuvent rendre le contexte vraiment difficile à trouver. Si on tape seulement le sigle dans Google, on obtient assez souvent en majorité des résultats presque sans rapport