Nachdem 64-Bit-Hardware verfügbar wurde, wurde die Notwendigkeit, größere Adressräume (größer als 232 Bytes) wurde offensichtlich. Da einige Anbieter jetzt Server mit 64 TiB (oder mehr) Speicher anbieten, ermöglichen x86_64 und arm64 jetzt die Adressierung von Adressräumen größer als 248 Bytes (verfügbar mit der standardmäßigen 48-Bit-Adressunterstützung).
x86_64 adressierte diese Anwendungsfälle, indem es die Unterstützung für fünfstufige Seitentabellen sowohl in der Hardware als auch in der Software ermöglichte. Damit können Adressräume gleich 2 adressiert werden57 Bytes (Einzelheiten finden Sie unter x86: 5-Level-Paging-Aktivierung für v4.12). Es erhöht die Grenzen auf 128 PiB des virtuellen Adressraums und 4 PiB des physischen Adressraums.
arm64 erreichte dasselbe durch die Einführung von zwei neuen Architekturerweiterungen – ARMv8.2 LVA (Large Virtual Addressing) und ARMv8.2 LPA (Large Physical Addressing). Diese ermöglichen 4 PiB virtuellen Adressraum und 4 PiB physischen Adressraum (d. h. 252 Bits jeweils).
Mit ARMv8.2-Architekturerweiterungen, die in neuen arm64-CPUs verfügbar sind, werden die beiden neuen Hardwareerweiterungen jetzt in Open-Source-Software unterstützt.
Beginnend mit der Linux-Kernel-Version 5.4 wurde die Unterstützung für 52-Bit (Large) Virtual Address (VA) und Physical Address (PA) für die arm64-Architektur eingeführt. Obwohl die Kernel-Dokumentation diese Funktionen beschreibt und wie sie sich auf die neuen Kernel auswirken, die auf älteren CPUs (die keine 52-Bit-VA-Erweiterung in der Hardware unterstützen) und neueren CPUs (die 52-Bit-VA-Erweiterungen in der Hardware unterstützen) ausgeführt werden, kann dies der Fall sein für durchschnittliche Benutzer komplex, sie zu verstehen und wie sie sich für den Empfang von VAs aus einem 52-Bit-Raum “anmelden” können.
Daher werde ich diese relativ neuen Konzepte in diesem Artikel vorstellen:
- Wie das Kernel-Speicherlayout für Arm64 „umgedreht“ wurde, nachdem die Unterstützung für diese Funktionen hinzugefügt wurde
- Die Auswirkungen auf Userspace-Anwendungen, insbesondere diejenigen, die Debugging-Unterstützung bieten (z. B. kexec-tools, makedumpfile und crash-utility)
- Wie sich Userspace-Anwendungen für den Empfang von VAs aus einem 52-Bit-Raum “anmelden” können, indem sie einen mmap-Hinweisparameter angeben, der größer als 48 Bit ist
LVA- und LPA-Erweiterungen der ARMv8.2-Architektur
Die ARMv8.2-Architektur bietet zwei wichtige Erweiterungen: Large Virtual Addressing (LVA) und Large Physical Addressing (LPA).
ARMv8.2-LVA unterstützt einen größeren VA-Speicherplatz für jedes Basisregister der Übersetzungstabelle von bis zu 52 Bit, wenn das 64-KB-Übersetzungsgranulat verwendet wird.
ARMv8.2-LPA ermöglicht:
- Eine größere physische Zwischenadresse (IPA) und PA-Speicherplatz von bis zu 52 Bits bei Verwendung des 64-KB-Übersetzungsgranulats
- Eine Blockgröße der Ebene 1, bei der der Block einen 4-TB-Adressbereich für das 64-KB-Übersetzungsgranulat abdeckt, wenn die Implementierung 52-Bit-PA unterstützt
Beachten Sie, dass diese Funktionen nur im AArch64-Zustand unterstützt werden.
Derzeit unterstützen die folgenden Arm64-Cortex-A-Prozessoren ARMv8.2-Erweiterungen:
- Cortex-A55
- Cortex-A75
- Cortex-A76
Weitere Einzelheiten finden Sie im Armv8 Architecture Reference Manual.
Kernel-Speicherlayout auf Arm64
Mit der ARMv8.2-Erweiterung, die Unterstützung für LVA-Speicherplatz hinzufügt (der nur verfügbar ist, wenn er mit einer Seitengröße von 64 KB ausgeführt wird), wird die Anzahl der Deskriptoren auf der ersten Übersetzungsebene erweitert.
Benutzeradressen haben die Bits 63:48 auf 0 gesetzt, während die Kerneladressen die gleichen Bits auf 1 gesetzt haben. Die TTBRx-Auswahl wird durch Bit 63 der virtuellen Adresse gegeben. Der swapper_pg_dir
enthält nur (globale) Kernel-Mappings, während die user pgd
enthält nur (nicht globale) Benutzerzuordnungen. Der swapper_pg_dir
Adresse wird in TTBR1 geschrieben und niemals in TTBR0 geschrieben.
AArch64-Linux-Speicherlayout mit 64-KB-Seiten plus drei Ebenen (52-Bit mit Hardwareunterstützung):
Start End Size Use
-----------------------------------------------------------------------
0000000000000000 000fffffffffffff 4PB user
fff0000000000000 fff7ffffffffffff 2PB kernel logical memory map
fff8000000000000 fffd9fffffffffff 1440TB [gap]
fffda00000000000 ffff9fffffffffff 512TB kasan shadow region
ffffa00000000000 ffffa00007ffffff 128MB bpf jit region
ffffa00008000000 ffffa0000fffffff 128MB modules
ffffa00010000000 fffff81ffffeffff ~88TB vmalloc
fffff81fffff0000 fffffc1ffe58ffff ~3TB [guard region]
fffffc1ffe590000 fffffc1ffe9fffff 4544KB fixed mappings
fffffc1ffea00000 fffffc1ffebfffff 2MB [guard region]
fffffc1ffec00000 fffffc1fffbfffff 16MB PCI I/O space
fffffc1fffc00000 fffffc1fffdfffff 2MB [guard region]
fffffc1fffe00000 ffffffffffdfffff 3968GB vmemmap
ffffffffffe00000 ffffffffffffffff 2MB [guard region]
Übersetzungstabellensuche mit 4-KB-Seiten:
+--------+--------+--------+--------+--------+--------+--------+--------+
|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
+--------+--------+--------+--------+--------+--------+--------+--------+
| | | | | |
| | | | | v
| | | | | [11:0] in-page offset
| | | | +-> [20:12] L3 index
| | | +-----------> [29:21] L2 index
| | +---------------------> [38:30] L1 index
| +-------------------------------> [47:39] L0 index
+-------------------------------------------------> [63] TTBR0/1
Übersetzungstabellensuche mit 64-KB-Seiten:
+--------+--------+--------+--------+--------+--------+--------+--------+
|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
+--------+--------+--------+--------+--------+--------+--------+--------+
| | | | |
| | | | v
| | | | [15:0] in-page offset
| | | +----------> [28:16] L3 index
| | +--------------------------> [41:29] L2 index
| +-------------------------------> [47:42] L1 index (48-bit)
| [51:42] L1 index (52-bit)
+-------------------------------------------------> [63] TTBR0/1
arm64-multi-level-translation.png
opensource.com
52-Bit-VA-Unterstützung im Kernel
Da die neueren Kernel mit LVA-Unterstützung gut auf älteren CPUs (die keine LVA-Erweiterung in der Hardware unterstützen) und den neueren CPUs (die LVA-Erweiterung in der Hardware unterstützen) laufen sollten, besteht der gewählte Designansatz darin, eine einzelne Binärdatei zu haben, die unterstützt 52 Bit (und muss beim frühen Booten auf 48 Bit zurückgreifen können, wenn die Hardwarefunktion nicht vorhanden ist). Das heißt, die VMEMMAP muss groß genug für 52-Bit-VAs bemessen sein und muss auch groß genug bemessen sein, um eine feste Größe aufzunehmen PAGE_OFFSET
.
Dieser Entwurfsansatz erfordert, dass der Kernel die folgenden Variablen für den neuen virtuellen Adressraum unterstützt:
VA_BITS constant the *maximum* VA space size
vabits_actual variable the *actual* VA space size
Also, während VA_BITS
bezeichnet die maximale Größe des VA-Speicherplatzes, der tatsächlich unterstützte VA-Speicherplatz (abhängig vom beim Booten vorgenommenen Schalter) wird durch angezeigt vabits_actual
.
Spiegeln des Kernel-Speicherlayouts
Der Entwurfsansatz, eine einzelne Kernel-Binärdatei beizubehalten, erfordert, dass sich die Kernel-.text in den höheren Adressen befindet, sodass sie gegenüber 48/52-Bit-VAs invariant sind. Da der Schatten des Kernel Address Sanitizer (KASAN) einen Bruchteil des gesamten Kernel-VA-Speicherplatzes ausmacht, muss sich das Ende des KASAN-Schattens auch in der oberen Hälfte des Kernel-VA-Speicherplatzes für 48- und 52-Bit befinden. (Beim Umschalten von 48 Bit auf 52 Bit ist das Ende des KASAN-Schattens unveränderlich und abhängig von ~0UL
, während die Startadresse zu den niedrigeren Adressen hin “wächst”).
Optimieren phys_to_virt()
und virt_to_phys()
, das PAGE_OFFSET
konstant gehalten wird 0xFFF0000000000000
(entspricht 52 Bit), dies vermeidet die Notwendigkeit eines zusätzlichen Variablenlesens. Der physvirt
und vmemmap
Offsets werden beim frühen Booten berechnet, um diese Logik zu aktivieren.
Betrachten Sie die folgende Konvertierung des physischen vs. virtuellen RAM-Adressraums:
/*
* The linear kernel range starts at the bottom of the virtual address
* space. Testing the top bit for the start of the region is a
* sufficient check and avoids having to worry about the tag.
*/
#define virt_to_phys(addr) ({
if (!(((u64)addr) & BIT(vabits_actual - 1)))
(((addr) & ~PAGE_OFFSET) + PHYS_OFFSET)
})
#define phys_to_virt(addr) ((unsigned long)((addr) - PHYS_OFFSET) | PAGE_OFFSET)
where:
PAGE_OFFSET - the virtual address of the start of the linear map, at the
start of the TTBR1 address space,
PHYS_OFFSET - the physical address of the start of memory, and
vabits_actual - the *actual* VA space size
Auswirkungen auf Userspace-Anwendungen, die zum Debuggen des Kernels verwendet werden
Mehrere Userspace-Anwendungen werden verwendet, um laufende/aktive Kernel zu debuggen oder den vmcore-Dump von einem abstürzenden System zu analysieren (z. B. um die Grundursache des Kernel-Absturzes zu ermitteln): kexec-tools, makedumpfile und crash-utility.
Wenn diese zum Debuggen des Arm64-Kernels verwendet werden, hat dies auch Auswirkungen auf sie, da die Speicherzuordnung des Arm64-Kernels “umgedreht” wird. Diese Anwendungen müssen auch einen Translation Table Walk durchführen, um eine physische Adresse zu bestimmen, die einer virtuellen Adresse entspricht (ähnlich wie es im Kernel gemacht wird).
Dementsprechend müssen Userspace-Anwendungen modifiziert werden, da sie stromaufwärts gebrochen werden, nachdem der “Flip” in der Kernel-Speicherabbildung eingeführt wurde.
Ich habe Korrekturen in den drei betroffenen Userspace-Anwendungen vorgeschlagen; Während einige Upstream akzeptiert wurden, stehen andere noch aus:
- Vorgeschlagene Makedumpfile-Upstream-Korrektur
- Vorgeschlagene Upstream-Korrektur für kexec-tools
- Fix im Crash-Utility akzeptiert
Sofern diese Änderungen nicht in Userspace-Anwendungen vorgenommen werden, bleiben sie für das Debuggen von laufenden/Live-Kernels oder das Analysieren des vmcore-Dumps von einem abstürzenden System defekt.
52-Bit-Userspace-VAs
Um die Kompatibilität mit Userspace-Anwendungen aufrechtzuerhalten, die auf der maximalen Größe des VA-Speicherplatzes von ARMv8.0 von 48 Bit basieren, gibt der Kernel standardmäßig virtuelle Adressen aus einem 48-Bit-Bereich an den Userspace zurück.
Userspace-Anwendungen können sich für den Empfang von VAs aus einem 52-Bit-Raum “anmelden”, indem sie einen mmap-Hinweisparameter angeben, der größer als 48 Bit ist.
Beispielsweise:
.mmap_high_addr.c
----
maybe_high_address = mmap(~0UL, size, prot, flags,...);
Es ist auch möglich, einen Debug-Kernel zu bauen, der Adressen aus einem 52-Bit-Bereich zurückgibt, indem Sie die folgenden Kernel-Konfigurationsoptionen aktivieren:
CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
Beachten Sie, dass diese Option nur zum Debuggen von Anwendungen gedacht ist und sollte nicht in der Produktion eingesetzt werden.
Schlussfolgerungen
Zusammenfassen:
- Ab der Linux-Kernel-Version 5.14 werden die neuen Armv8.2-Hardwareerweiterungen LVA und LPA jetzt gut im Linux-Kernel unterstützt.
- Userspace-Anwendungen wie kexec-tools und makedumpfile, die zum Debuggen des Kernels verwendet werden, sind defekt im Augenblick und Warten auf die Annahme von Upstream-Korrekturen.
- Legacy-Userspace-Anwendungen, die auf dem Arm64-Kernel basieren und ihm eine 48-Bit-VA bereitstellen, funktionieren unverändert weiter, während neuere Userspace-Anwendungen sich für den Empfang von VAs aus einem 52-Bit-Bereich „anmelden“ können, indem sie einen größeren mmap-Hinweisparameter angeben als 48 Bit.
Dieser Artikel stützt sich auf das Speicherlayout unter AArch64 Linux und die Linux-Kernel-Dokumentation v5.9.12. Beide sind unter GPLv2.0 lizenziert.