Portage du système d'exploitation vers Aarch64

Aarch64 est une architecture 64 bits d'ARM (parfois appelée arm64). Dans cet article, je vais vous expliquer en quoi il diffère de l'ARM «normal» (32 bits) et à quel point il est difficile d'y porter votre système.


Cet article n'est pas un guide détaillé, mais plutôt un aperçu des modules système qui devront être refaits et de la différence entre l'architecture dans son ensemble et les ARM 32 bits ordinaires; tout cela à partir de mon expérience personnelle de portage d' Embox sur cette architecture. Pour le portage direct d'un système spécifique, d'une manière ou d'une autre vous devrez vous occuper de la documentation, à la fin de l'article j'ai laissé des liens vers certains documents qui peuvent être utiles.


En fait, il y a plus de différences que de similitudes, et Aarch64 est plus une nouvelle architecture qu'une extension 64 bits de l'ARM familier. Le prédécesseur d'Aarch64 est en grande partie Aarch32 (il s'agit d'une extension de l'ARM 32 bits habituel), mais comme je n'en avais pas l'expérience, je n'écrirai pas à ce sujet :)


Plus loin dans l'article, si j'écris sur "l'ancien" ou "l'ancien" ARM, je veux dire l'ARM 32 bits (avec un ensemble de commandes ARM).


Je vais brièvement parcourir la liste des modifications par rapport à l'ARM 32 bits, puis je les analyserai plus en détail.


  • Les registres à usage général sont devenus 2 fois plus larges (ils sont maintenant 64 bits chacun), et leur nombre a doublé (c'est-à-dire qu'il n'y en a plus 16, mais 32).
  • Refus du concept de registres coprocesseurs, désormais accessibles simplement par nom, par exemple msr vbar_el1, x0 (contre le précédent mcr p15, 0, %0, c1, c1, 2 )
  • Le nouveau modèle MMU (il n'est en aucun cas lié à l'ancien, vous devez réécrire).
  • Auparavant, il y avait deux niveaux de privilège: les modes utilisateur (correspondant au mode processeur USR) et système (correspondant aux systèmes SYS, IRQ, FIQ, ABT, ...), maintenant tout est à la fois plus facile et plus compliqué en même temps - il y a maintenant 4 modes.
  • AdvSIMD a remplacé NEON, les opérations en virgule flottante sont effectuées via celui-ci.

Maintenant plus sur les points.


Registres et jeu d'instructions


Les registres à usage général sont r0-r30, et vous pouvez y accéder en 64 bits (x0-x30) ou en 32 bits (w0-w30, accès aux 32 bits inférieurs).


L'ensemble d'instructions pour Aarch64 est appelé A64. Lisez la description des instructions ici . L'arithmétique de base et certaines autres commandes en langage assembleur sont restées les mêmes:


  mov w0, w1 /*    w1  w0 */ add x0, x1, 13 /*   x0  x1   13 */ b label /* ""   "label" bl label /* ""   "label",     x30 */ ldr x3, [x1, 0] /*   x3 ,    x1 */ str x3, [x0, 0] /*   x3  ,    x0 */ 

Maintenant, un peu sur les différences:


  • Un registre spécial «zéro» rzr/xzr/wzr , qui est zéro lors de la lecture (vous pouvez utiliser l'écriture dans le registre, mais le résultat du calcul ne sera écrit nulle part).

 subs xzr, x1, x2 /*  x1  x2    NZCV,       */ 

  • Vous ne pouvez pas empiler plusieurs registres ( stmfd sp!, {r0-r3} ) dans la pile à la fois, vous devez le faire par paires:

  stp x0, x1, [sp, 16]! stp x2, x3, [sp, 16]! 

  • Le registre PC (compteur de programmes, pointeur sur l'instruction d'exécution en cours) n'est plus un registre général (anciennement R15), par conséquent, il n'est pas accessible avec des commandes ordinaires ( mov , ldr ), uniquement via ret , bl , etc.


  • L'état du programme affiche désormais non pas CPSR (ce registre n'existe tout simplement pas), mais les registres DAIF (contient l'IRQ, le masque FIQ, etc., AIF - les mêmes bits A, I, F de CPSR), NZCV (bits négatifs, zéro, report , oVerflow - tout d'un coup, le même NZCV de CPSR) et le registre de contrôle du système (SCTLR, pour activer la mise en cache, MMU, endianness, etc.).



Il semble que ces commandes soient suffisantes pour écrire un simple chargeur de démarrage qui peut transférer le contrôle vers un code indépendant de la plate-forme :)


Modes de performance et commutation entre eux


Les modes de performance sont bien écrits dans Fundamentals of ARMv8-A , je vais brièvement raconter l'essentiel de ce document ici.


Aarch64 possède 4 niveaux de privilèges (niveau d'exécution, ci-après abrégé EL).


  • EL3 - Secure Monitor (on suppose que le micrologiciel fonctionne à ce niveau)
  • EL2 - Hyperviseur
  • EL1 - OS
  • EL0 - Applications

Sur un système d'exploitation 64 bits, vous pouvez exécuter des applications 32 bits et 64 bits; sur un système d'exploitation 32 bits, seules les applications 32 bits peuvent être exécutées.



Les transitions entre EL se font soit à l'aide d'exceptions (appels système, interruptions, erreur d'accès à la mémoire), soit à l'aide de la commande return from exception ( eret ).


Chaque EL a ses propres registres SPSR, ELR, SP (c'est-à-dire qu'il s'agit de "registres en banque").


De nombreux registres système sont également divisés par EL - par exemple, le registre de contexte MMU ttbr0 - est ttbr0_el2 , ttbr0_el1 , et vous devez accéder à votre registre sur l'EL correspondant. Il en va de même pour les registres d'état des programmes - DAIF, NZCV, SCTLR, SPSR, ELR ...


MMU


Armv8-A prend en charge la MMU ARMv8.2 LPA, vous trouverez plus d'informations à ce sujet dans le chapitre D5 du manuel de référence de l'architecture ARM pour Armv8, Armv8-A .


En bref, cette MMU prend en charge les pages de 4 Ko (4 niveaux de tables de mémoire virtuelle), 16 Ko (4 niveaux) et 64 Ko (3 niveaux). À n'importe quel niveau intermédiaire, vous pouvez spécifier un bloc de mémoire, indiquant ainsi non pas le niveau suivant de la table, mais un morceau entier de mémoire de la taille que la table de niveau suivante devrait «couvrir». J'ai un article de longue date sur la mémoire virtuelle, où vous pouvez lire des informations sur les tableaux, les niveaux de traduction, et c'est tout.


Parmi les petits changements, ils ont refusé les domaines, mais ont ajouté des indicateurs comme un bit sale.


En général, à l'exception des "blocs" au lieu des tables de traduction intermédiaires, aucun changement conceptuel spécial n'a été remarqué, MMU comme MMU.


Simd avancé


Il existe d'importantes différences AdvSIMD dans l'ancien NEON, à la fois lors du travail avec virgule flottante et avec les opérations vectorielles (SIMD). Par exemple, si auparavant D0 était composé de S0 et S1, et Q0 - de D0 et D1, alors ce n'est plus le cas: Q0 correspond à D0 et S0, pour Q1 - D1 et S0, et ainsi de suite. Dans le même temps, la prise en charge de VFP / SIMD est obligatoire, en appelant l'accord, il n'y a plus de transfert de paramètres programmatiques (ce que l'on appelait auparavant "soft float ABI", dans GCC - l'indicateur -mfloat-abi=softfp ), vous devez donc implémenter la prise en charge matérielle pour virgule flottante .


Il y avait 16 registres de 128 bits:



Il y a 32 registres de 128 bits chacun:



Vous pouvez en savoir plus sur NEON dans cet article ; une liste des commandes disponibles pour Aarch64 peut être trouvée ici .


Opérations de base avec des registres à virgule flottante:


  fadd s0, s1, s2 /* s0 = s1 + s2 */ fmul d0, d1, d2 /* d0 = d1 * d2 */ 

Opérations SIMD de base:


  /*  , : NEON,    */ /* q0 = q1 + q2,   --   4     */ vadd.s32 q0, q1, q2 /* : AdvSIMD,    */ /* v0 = v1 + v2,   --   4     */ add v0.4s, v1.4s, v2.4s /*   v1 (  2 64- )    d1 */ addv d1, v1.ds /*     4   0 */ movi v1.4s, 0x0 

Plateformes


QEMU


QEMU prend en charge Aarch64. L'une des plates-formes est virt , pour qu'il démarre en mode 64 bits, vous devez également passer le -cpu cortex-a53 , quelque chose comme ceci:


 qemu-system-aarch64 -M virt -cpu cortex-a53 -kernel ./embox -m 1024 -nographic # ./embox -- ELF-  

Ce qui est bien, beaucoup de périphériques sont utilisés pour cette plate-forme, dont les pilotes étaient déjà dans Embox - par exemple, PL011 pour la console, ARM Generic Interrupt Controller, etc. Bien sûr, ces appareils ont des adresses de registre de base différentes et d'autres numéros d'interruption, mais l'essentiel est le code du pilote fonctionne inchangé sur la nouvelle architecture. Lorsque le système démarre, la commande est en EL1.


i.MX8


En raison de ce morceau de fer, le portage vers Aarch64 - i.MX8MQ Nitrogen8M a été démarré.



Contrairement à QEMU, u-boot transfère le contrôle à l'image dans EL2 et, en outre, pour une raison quelconque, il inclut MMU (toute la mémoire est mappée 1 à 1), ce qui crée des problèmes supplémentaires lors de l'initialisation.


Embox a déjà pris en charge i.MX6, et, bien, dans i.MX8, la partie de la périphérie est la même - par exemple, UART et Ethernet, qui a également fonctionné (j'ai dû corriger quelques endroits où il y avait une liaison étroite aux adresses 32 bits). D'un autre côté, le contrôleur d'interruption y est différent - ARM GICv3, qui est assez différent de la première version.


Conclusion


Pour le moment, la prise en charge d'Aarch64 dans Embox n'est pas complète, mais il existe déjà une fonctionnalité minimale - interruptions, MMU, entrée-sortie via UART. Beaucoup reste à finaliser, mais les premières étapes ont été plus faciles à réaliser qu'il n'y paraissait dès le début. Il y a beaucoup moins de documentation et d'articles que sur ARM, mais il y a plus qu'assez d'informations pour tout gérer.


En général, si vous avez de l'expérience avec ARM, le portage vers Aarch64 est une tâche réalisable. Bien que, comme d'habitude, vous pouvez tomber sur quelques petites choses :)


Vous pouvez télécharger le projet pour le piquer dans QEMU à partir de notre référentiel , si vous avez des questions - écrivez dans les commentaires, ou dans la newsletter , ou dans le chat dans le Telegram (il y a aussi un canal ).


Liens utiles



PS


Du 24 au 25 août, nous parlerons à TechTrain , écouterons nos performances environ deux ou trois fois , venez au stand - nous répondrons à vos questions :)

Source: https://habr.com/ru/post/fr463417/


All Articles