Dopo che l'hardware a 64 bit è diventato disponibile, è diventata ovvia la necessità di gestire spazi di indirizzi più grandi (maggiori di 2 byte). Con alcuni fornitori che ora offrono server con 64 TiB (o più) di memoria, x86_64 e arm64 ora consentono di indirizzare spazi di indirizzi maggiori di 2 byte (disponibile con il supporto di indirizzi a 48 bit predefinito).
x86_64 ha affrontato questi casi d'uso abilitando il supporto per tabelle di pagine a cinque livelli sia nell'hardware che nel software. Ciò consente di indirizzare spazi di indirizzi pari a 2 byte (vedere x86:abilitazione del paging a 5 livelli per v4.12 per i dettagli). Supera i limiti a 128PiB di spazio di indirizzi virtuali e 4PiB di spazio di indirizzi fisici.
arm64 ha ottenuto lo stesso risultato introducendo due nuove estensioni dell'architettura:ARMv8.2 LVA (Large Virtual Addressing) e ARMv8.2 LPA (Large Physical Addressing). Questi consentono 4 PiB di spazio di indirizzi virtuali e 4 PiB di spazio di indirizzi fisici (cioè 2 bit ciascuno, rispettivamente).
Più risorse Linux
- Comandi Linux cheat sheet
- Cheat sheet sui comandi avanzati di Linux
- Corso online gratuito:Panoramica tecnica RHEL
- Cheat sheet della rete Linux
- Cheat sheet di SELinux
- Cheat sheet dei comandi comuni di Linux
- Cosa sono i container Linux?
- I nostri ultimi articoli su Linux
Con le estensioni dell'architettura ARMv8.2 disponibili nelle nuove CPU arm64, le due nuove estensioni hardware sono ora supportate nel software open source.
A partire dalla versione del kernel Linux 5.4, è stato introdotto il supporto per indirizzo virtuale (VA) e indirizzo fisico (PA) a 52 bit per l'architettura arm64. Sebbene la documentazione del kernel descriva queste funzionalità e il modo in cui influiscono sui nuovi kernel in esecuzione su CPU meno recenti (che non supportano l'estensione VA a 52 bit nell'hardware) e CPU più recenti (che supportano estensioni VA a 52 bit nell'hardware), può essere complesso per gli utenti medi per capirli e come possono "attivare" la ricezione di VA da uno spazio a 52 bit.
Pertanto, introdurrò questi concetti relativamente nuovi in questo articolo:
- Come è stato "capovolto" il layout della memoria del kernel per Arm64 dopo l'aggiunta del supporto per queste funzionalità
- L'impatto sulle applicazioni dello spazio utente, in particolare quelle che forniscono supporto per il debug (ad es. kexec-tools, makedumpfile e crash-utility)
- In che modo le applicazioni dello spazio utente possono "accettare" la ricezione di VA da uno spazio a 52 bit specificando un parametro di suggerimento mmap più grande di 48 bit
Estensioni LVA e LPA dell'architettura ARMv8.2
L'architettura ARMv8.2 fornisce due importanti estensioni:Large Virtual Addressing (LVA) e Large Physical Addressing (LPA).
ARMv8.2-LVA supporta uno spazio VA più ampio per ogni registro di base della tabella di traduzione fino a 52 bit quando si utilizza il granulo di traduzione da 64 KB.
ARMv8.2-LPA consente:
- Un indirizzo fisico intermedio (IPA) e uno spazio PA più grandi fino a 52 bit quando si utilizza il granulo di traduzione da 64 KB
- Una dimensione del blocco di livello 1 in cui il blocco copre un intervallo di indirizzi di 4 TB per il granello di traduzione da 64 KB se l'implementazione supporta 52 bit di PA
Tieni presente che queste funzionalità sono supportate solo nello stato AArch64.
Attualmente, i seguenti processori Arm64 Cortex-A supportano le estensioni ARMv8.2:
- Corteccia-A55
- Corteccia-A75
- Corteccia-A76
Per maggiori dettagli, vedere il Manuale di riferimento dell'architettura Armv8.
Layout della memoria del kernel su Arm64
Con l'estensione ARMv8.2 che aggiunge il supporto per lo spazio LVA (disponibile solo quando si esegue con una dimensione della pagina di 64 KB), il numero di descrittori viene ampliato nel primo livello di traduzione.
Gli indirizzi utente hanno i bit 63:48 impostati a 0, mentre gli indirizzi del kernel hanno gli stessi bit impostati a 1. La selezione TTBRx è data dal bit 63 dell'indirizzo virtuale. La swapper_pg_dir
contiene solo mappature del kernel (globali), mentre l'utente pgd
contiene solo mappature utente (non globali). La swapper_pg_dir
l'indirizzo viene scritto in TTBR1 e mai in TTBR0.
Layout di memoria Linux AArch64 con pagine da 64 KB più tre livelli (52 bit con supporto hardware):
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]
Ricerca nella tabella di traduzione con pagine da 4 KB:
+--------+--------+--------+--------+--------+--------+--------+--------+
|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
Ricerca nella tabella di traduzione con pagine da 64 KB:
+--------+--------+--------+--------+--------+--------+--------+--------+
|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
Supporto VA a 52 bit nel kernel
Poiché i kernel più recenti con supporto LVA dovrebbero funzionare bene su CPU più vecchie (che non supportano l'estensione LVA nell'hardware) e sulle CPU più recenti (che supportano l'estensione LVA nell'hardware), l'approccio progettuale scelto consiste nell'avere un singolo binario che supporti 52 bit (e deve essere in grado di tornare a 48 bit all'avvio anticipato se la funzionalità hardware non è presente). Cioè, il VMEMMAP deve essere sufficientemente grande per VA a 52 bit e deve anche essere sufficientemente grande da contenere un PAGE_OFFSET
fisso .
Questo approccio di progettazione richiede che il kernel supporti le seguenti variabili per il nuovo spazio di indirizzi virtuali:
VA_BITS constant the *maximum* VA space size
vabits_actual variable the *actual* VA space size
Quindi, mentre VA_BITS
indica la dimensione massima dello spazio VA, lo spazio VA effettivo supportato (a seconda del passaggio effettuato all'avvio) è indicato da vabits_actual
.
Spostamento del layout della memoria del kernel
L'approccio progettuale per mantenere un singolo binario del kernel richiede che il kernel .text si trovi negli indirizzi più alti, in modo tale che siano invarianti rispetto ai VA a 48/52 bit. Poiché l'ombra KASAN (Kernel Address Sanitizer) è una frazione dell'intero spazio VA del kernel, la fine dell'ombra KASAN deve trovarsi anche nella metà superiore dello spazio VA del kernel sia per 48 che per 52 bit. (Passando da 48 bit a 52 bit, la fine dell'ombra KASAN è invariante e dipende da ~0UL
, mentre l'indirizzo iniziale "crescerà" verso gli indirizzi inferiori).
Per ottimizzare phys_to_virt()
e virt_to_phys()
, il PAGE_OFFSET
viene mantenuto costante a 0xFFF0000000000000
(corrispondente a 52 bit), ciò ovvia alla necessità di una lettura di variabile aggiuntiva. Il physvirt
e vmemmap
gli offset vengono calcolati all'avvio anticipato per abilitare questa logica.
Considera la seguente conversione dello spazio degli indirizzi della RAM fisica e virtuale:
/*
* 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
Impatto sulle applicazioni userspace utilizzate per eseguire il debug del kernel
Diverse applicazioni dello spazio utente vengono utilizzate per eseguire il debug di kernel in esecuzione/live o analizzare il dump di vmcore da un sistema in crash (ad esempio, per determinare la causa principale del crash del kernel):kexec-tools, makedumpfile e crash-utility.
Quando questi vengono utilizzati per il debug del kernel Arm64, c'è anche un impatto su di essi a causa della mappa di memoria del kernel Arm64 che viene "capovolta". Queste applicazioni devono anche eseguire una passeggiata della tabella di traduzione per determinare un indirizzo fisico corrispondente a un indirizzo virtuale (simile a come viene fatto nel kernel).
Di conseguenza, le applicazioni dello spazio utente devono essere modificate poiché vengono interrotte a monte dopo l'introduzione del "flip" nella mappa di memoria del kernel.
Ho proposto correzioni nelle tre applicazioni dello spazio utente interessate; mentre alcuni sono stati accettati a monte, altri sono ancora in attesa:
- Proposta correzione upstream di makedumpfile
- Proposta soluzione a monte di kexec-tools
- Correzione accettata in crash-utility
A meno che queste modifiche non vengano apportate nelle applicazioni dello spazio utente, rimarranno interrotte per il debug di kernel in esecuzione/live o per l'analisi del dump di vmcore da un sistema in crash.
VA spazio utente a 52 bit
Per mantenere la compatibilità con le applicazioni dello spazio utente che si basano sulla dimensione massima dello spazio VA ARMv8.0 di 48 bit, il kernel, per impostazione predefinita, restituirà gli indirizzi virtuali allo spazio utente da un intervallo di 48 bit.
Le applicazioni Userspace possono "accettare" la ricezione di VA da uno spazio a 52 bit specificando un parametro hint mmap maggiore di 48 bit.
Ad esempio:
.mmap_high_addr.c
----
maybe_high_address = mmap(~0UL, size, prot, flags,...);
È anche possibile creare un kernel di debug che restituisca indirizzi da uno spazio a 52 bit abilitando le seguenti opzioni di configurazione del kernel:
CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
Tieni presente che questa opzione è intesa solo per il debug di applicazioni e non essere utilizzato nella produzione.
Conclusioni
Per riassumere:
- A partire dalla versione del kernel Linux 5.14, le nuove estensioni hardware Armv8.2 LVA e LPA sono ora ben supportate nel kernel Linux.
- Le applicazioni dello spazio utente come kexec-tools e makedumpfile usate per il debug del kernel sono rotte al momento e in attesa di accettazione delle correzioni a monte.
- Le applicazioni legacy dello spazio utente che si basano sul kernel Arm64 fornendo un VA a 48 bit continueranno a funzionare così com'è, mentre le applicazioni più recenti dello spazio utente possono "accettare" la ricezione di VA da uno spazio a 52 bit specificando un parametro hint mmap che è maggiore di 48 bit.
Questo articolo si basa su Memory Layout su AArch64 Linux e documentazione del kernel Linux v5.9.12. Entrambi sono concessi in licenza sotto GPLv2.0.