Portierung des Betriebssystems auf Aarch64

Aarch64 ist eine 64-Bit-Architektur von ARM (manchmal auch als arm64 bezeichnet). In diesem Artikel werde ich Ihnen erklären, wie es sich vom "normalen" (32-Bit) ARM unterscheidet und wie schwierig es ist, Ihr System darauf zu portieren.


Dieser Artikel ist kein detaillierter Leitfaden, sondern eine Übersicht über die Systemmodule, die überarbeitet werden müssen, und wie stark sich die Architektur insgesamt von normalen 32-Bit-ARMs unterscheidet. All dies aus meiner persönlichen Erfahrung mit der Portierung von Embox auf diese Architektur. Für die direkte Portierung eines bestimmten Systems müssen Sie sich auf die eine oder andere Weise mit der Dokumentation befassen. Am Ende des Artikels habe ich Links zu einigen Dokumenten hinterlassen, die möglicherweise nützlich sind.


Tatsächlich gibt es mehr Unterschiede als Ähnlichkeiten, und Aarch64 ist eher eine neue Architektur als eine 64-Bit-Erweiterung des bekannten ARM. Der Vorgänger von Aarch64 ist größtenteils Aarch32 (dies ist eine Erweiterung des üblichen 32-Bit-ARM), aber da ich keine Erfahrung damit hatte, werde ich nicht darüber schreiben :)


Wenn ich weiter im Artikel über den "alten" oder "alten" ARM schreibe, meine ich 32-Bit-ARM (mit einer Reihe von ARM-Befehlen).


Ich werde kurz die Liste der Änderungen im Vergleich zu 32-Bit-ARM durchgehen und sie dann genauer analysieren.


  • Allzweckregister sind zweimal breiter geworden (jetzt sind sie jeweils 64 Bit), und ihre Anzahl hat sich verdoppelt (das heißt, jetzt gibt es nicht 16, sondern 32).
  • Ablehnung des Konzepts der Coprozessor-Register, jetzt kann einfach über den Namen auf sie zugegriffen werden, z. B. msr vbar_el1, x0 (gegenüber dem vorherigen mcr p15, 0, %0, c1, c1, 2 ).
  • Das neue MMU-Modell (es ist in keiner Weise mit dem alten verbunden, Sie müssen erneut schreiben).
  • Früher gab es zwei Berechtigungsstufen: Benutzer (entsprechend dem USR-Prozessormodus) und Systemmodus (entsprechend den SYS-, IRQ-, FIQ-, ABT-, ...) Modi. Jetzt ist alles gleichzeitig einfacher und komplizierter - jetzt gibt es 4 Modi.
  • AdvSIMD ersetzte NEON, Gleitkommaoperationen werden dadurch durchgeführt.

Nun mehr zu den Punkten.


Register und Befehlssatz


Allzweckregister sind r0-r30, und Sie können auf sie als 64-Bit (x0-x30) oder als 32-Bit (w0-w30, Zugriff auf die unteren 32 Bit) zugreifen.


Der Befehlssatz für Aarch64 heißt A64. Lesen Sie hier die Beschreibung der Anleitung. Die Grundrechenarten und einige andere Befehle in Assemblersprache blieben gleich:


  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 */ 

Nun ein wenig zu den Unterschieden:


  • Es wurde ein spezielles "Null" -Register rzr/xzr/wzr , das beim Lesen Null ist (Sie können das Schreiben in das Register verwenden, aber das Ergebnis der Berechnung wird nirgendwo geschrieben).

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

  • Sie können nicht viele Register ( stmfd sp!, {r0-r3} ) gleichzeitig in den Stapel stapeln. Sie müssen dies paarweise tun:

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

  • Das PC-Register (Programmzähler, ein Zeiger auf den aktuell ausgeführten Befehl) ist jetzt kein allgemeines Register (es war früher R15), daher kann nicht mit normalen Befehlen ( mov , ldr ) nur über ret , bl usw. zugegriffen werden.


  • Der Programmstatus zeigt jetzt nicht CPSR an (dieses Register existiert einfach nicht), sondern DAIF-Register (enthält die IRQ, FIQ-Maske usw., AIF - die gleichen Bits A, I, F von CPSR), NZCV (Bits negativ, Null, Übertrag , oVerflow - plötzlich das gleiche NZCV von CPSR) und System Control Register (SCTLR, um Caching, MMU, Endianness usw. zu ermöglichen).



Es scheint, dass diese Befehle ausreichen, um einen einfachen Bootloader zu schreiben, der die Steuerung auf einen plattformunabhängigen Code übertragen kann :)


Leistungsmodi und Umschalten zwischen ihnen


Die Leistungsmodi sind in den Grundlagen von ARMv8-A gut geschrieben. Ich werde hier kurz auf das Wesentliche dieses Dokuments eingehen.


Aarch64 verfügt über 4 Berechtigungsstufen (Ausführungsstufe, im Folgenden als EL abgekürzt).


  • EL3 - Secure Monitor (es wird davon ausgegangen, dass die Firmware auf dieser Ebene ausgeführt wird)
  • EL2 - Hypervisor
  • EL1 - OS
  • EL0 - Anwendungen

Unter einem 64-Bit-Betriebssystem können Sie sowohl 32-Bit- als auch 64-Bit-Anwendungen ausführen. Auf einem 32-Bit-Betriebssystem können nur 32-Bit-Anwendungen ausgeführt werden.



Übergänge zwischen ELs werden entweder mit Hilfe von Ausnahmen (Systemaufrufe, Unterbrechungen, Speicherzugriffsfehler) oder mit Hilfe des eret return from exception ( eret ) durchgeführt.


Jede EL hat ihre eigenen Register SPSR, ELR, SP (dh es handelt sich um "Bankregister").


Viele Systemregister werden auch durch EL unterteilt - beispielsweise ist das MMU- ttbr0 - ttbr0_el2 , ttbr0_el1 , und Sie müssen auf Ihr Register in der entsprechenden EL zugreifen. Gleiches gilt für Programmstatusregister - DAIF, NZCV, SCTLR, SPSR, ELR ...


MMU


Armv8-A unterstützt MMU ARMv8.2 LPA. Weitere Informationen hierzu finden Sie im Kapitel D5 des ARM Architecture Reference Manual für Armv8, Armv8-A .


Kurz gesagt, diese MMU unterstützt 4-KB-Seiten (4 Ebenen virtueller Speichertabellen), 16 KB (4 Ebenen) und 64 KB (3 Ebenen). Auf jeder der Zwischenebenen können Sie einen Speicherblock angeben, der nicht die nächste Ebene der Tabelle angibt, sondern ein ganzes Stück Speicher der Größe, die die Tabelle der nächsten Ebene „abdecken“ soll. Ich habe einen langjährigen Artikel über virtuellen Speicher, in dem Sie über Tabellen, Übersetzungsebenen und das alles lesen können.


Von den kleinen Änderungen lehnten sie die Domains ab, fügten aber Flags wie Dirty Bit hinzu.


Im Allgemeinen wurden mit Ausnahme von "Blöcken" anstelle von Zwischenübersetzungstabellen keine besonderen konzeptionellen Änderungen festgestellt, MMU als MMU.


Fortgeschrittene simd


Es gibt signifikante AdvSIMD-Unterschiede im alten NEON, sowohl bei der Arbeit mit Gleitkomma- als auch mit Vektoroperationen (SIMD). Wenn zum Beispiel früher D0 aus S0 und S1 und Q0 - aus D0 und D1 bestand, dann ist es jetzt nicht so: Q0 entspricht D0 und S0 für Q1 - D1 und S0 und so weiter. Gleichzeitig ist die Unterstützung für VFP / SIMD obligatorisch, da durch Aufrufen der Vereinbarung jetzt keine programmatische Parameterübertragung mehr erfolgt (was in GCC früher als "soft float ABI" bezeichnet wurde - das Flag -mfloat-abi=softfp ), sodass Sie die Hardwareunterstützung für Gleitkomma implementieren müssen .


Es gab 16 Register mit 128 Bit:



Es gibt 32 Register mit jeweils 128 Bit:



Weitere Informationen zu NEON finden Sie in diesem Artikel . Eine Liste der verfügbaren Befehle für Aarch64 finden Sie hier .


Grundoperationen mit Gleitkommaregistern:


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

Grundlegende SIMD-Funktionen:


  /*  , : 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 

Plattformen


QEMU


QEMU unterstützt Aarch64. Eine der Plattformen ist virt , so dass sie im 64-Bit-Modus startet. Sie müssen zusätzlich das -cpu cortex-a53 , -cpu cortex-a53 so:


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

Was schön ist, für diese Plattform werden viele Peripheriegeräte verwendet, für die sich bereits Treiber in Embox befanden - zum Beispiel PL011 für die Konsole, ARM Generic Interrupt Controller usw. Natürlich haben diese Geräte unterschiedliche Basisregisteradressen und andere Interrupt-Nummern, aber die Hauptsache ist Der Treibercode funktioniert in der neuen Architektur unverändert. Wenn das System startet, befindet sich die Steuerung in EL1.


i.MX8


Aufgrund dieses Stücks Eisen wurde die Portierung auf Aarch64 - i.MX8MQ Nitrogen8M gestartet.



Im Gegensatz zu QEMU überträgt U-Boot die Steuerung auf das Image in EL2 und enthält außerdem aus irgendeinem Grund MMU (der gesamte Speicher ist 1 zu 1 zugeordnet), was bei der Initialisierung zu zusätzlichen Problemen führt.


Embox unterstützte i.MX6 bereits, und in i.MX8 ist der Teil der Peripherie derselbe - zum Beispiel UART und Ethernet, was ebenfalls funktionierte (ich musste einige Stellen reparieren, an denen eine enge Bindung zu 32-Bit-Adressen bestand). Andererseits ist der Interrupt-Controller dort anders - ARM GICv3, das sich stark von der ersten Version unterscheidet.


Fazit


Derzeit ist die Unterstützung für Aarch64 in Embox nicht vollständig, es gibt jedoch bereits minimale Funktionen - Interrupts, MMU, Eingabe / Ausgabe über UART. Es bleibt noch viel zu erledigen, aber die ersten Schritte waren einfacher, als es von Anfang an schien. Es gibt viel weniger Dokumentationen und Artikel als auf ARM, aber es gibt mehr als genug Informationen, um mit allem fertig zu werden.


Wenn Sie Erfahrung mit ARM haben, ist die Portierung auf Aarch64 im Allgemeinen eine praktikable Aufgabe. Obwohl man wie immer über ein paar Kleinigkeiten stolpern kann :)


Sie können das Projekt herunterladen, um es in QEMU aus unserem Repository zu speichern, wenn Sie Fragen haben - schreiben Sie in die Kommentare, in den Newsletter oder in den Chat im Telegramm (es gibt auch einen Kanal ).


Nützliche Links



PS


Vom 24. bis 25. August werden wir bei TechTrain sprechen, unsere Auftritte ungefähr zwei- oder dreimal anhören, zum Stand kommen - wir werden Ihre Fragen beantworten :)

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


All Articles