Después de que el hardware de 64 bits estuvo disponible, se hizo evidente la necesidad de manejar espacios de direcciones más grandes (más de 2 bytes). Con algunos proveedores que ahora ofrecen servidores con 64 TiB (o más) de memoria, x86_64 y arm64 ahora permiten direccionar espacios de direcciones de más de 2 bytes (disponible con el soporte de dirección predeterminado de 48 bits).
x86_64 abordó estos casos de uso al habilitar la compatibilidad con tablas de páginas de cinco niveles tanto en hardware como en software. Esto permite direccionar espacios de direcciones iguales a 2 bytes (ver x86:habilitación de paginación de 5 niveles para v4.12 para más detalles). Supera los límites a 128PiB de espacio de direcciones virtuales y 4PiB de espacio de direcciones físicas.
arm64 logró lo mismo al presentar dos nuevas extensiones de arquitectura:ARMv8.2 LVA (direccionamiento virtual grande) y ARMv8.2 LPA (direccionamiento físico grande). Estos permiten 4 PiB de espacio de direcciones virtuales y 4 PiB de espacios de direcciones físicas (es decir, 2 bits cada uno, respectivamente).
Más recursos de Linux
- Hoja de trucos de los comandos de Linux
- Hoja de trucos de comandos avanzados de Linux
- Curso en línea gratuito:Descripción general técnica de RHEL
- Hoja de trucos de red de Linux
- Hoja de trucos de SELinux
- Hoja de trucos de los comandos comunes de Linux
- ¿Qué son los contenedores de Linux?
- Nuestros últimos artículos sobre Linux
Con las extensiones de arquitectura ARMv8.2 disponibles en las nuevas CPU arm64, las dos nuevas extensiones de hardware ahora son compatibles con el software de código abierto.
A partir de la versión 5.4 del kernel de Linux, se introdujo la compatibilidad con la dirección virtual (VA) y la dirección física (PA) de 52 bits (grandes) para la arquitectura arm64. Aunque la documentación del núcleo describe estas funciones y cómo afectan a los nuevos núcleos que se ejecutan en CPU más antiguas (que no admiten la extensión VA de 52 bits en hardware) y CPU más nuevas (que admiten extensiones VA de 52 bits en hardware), puede ser complejo para que los usuarios promedio los entiendan y cómo pueden "optar por participar" para recibir VA desde un espacio de 52 bits.
Por lo tanto, presentaré estos conceptos relativamente nuevos en este artículo:
- Cómo se "cambió" el diseño de la memoria del kernel para Arm64 después de que se agregó la compatibilidad con estas funciones
- El impacto en las aplicaciones del espacio de usuario, especialmente las que brindan compatibilidad con la depuración (por ejemplo, herramientas kexec, makedumpfile y utilidad de bloqueo)
- Cómo las aplicaciones del espacio de usuario pueden "optar" por recibir VA desde un espacio de 52 bits especificando un parámetro de sugerencia mmap que es mayor a 48 bits
Arquitectura ARMv8.2 LVA y extensiones LPA
La arquitectura ARMv8.2 proporciona dos extensiones importantes:direccionamiento virtual grande (LVA) y direccionamiento físico grande (LPA).
ARMv8.2-LVA admite un espacio VA más grande para cada registro base de la tabla de traducción de hasta 52 bits cuando se usa el gránulo de traducción de 64 KB.
ARMv8.2-LPA permite:
- Una dirección física intermedia (IPA) más grande y un espacio PA de hasta 52 bits cuando se usa el gránulo de traducción de 64 KB
- Un tamaño de bloque de nivel 1 donde el bloque cubre un rango de direcciones de 4 TB para el gránulo de traducción de 64 KB si la implementación admite 52 bits de PA
Tenga en cuenta que estas funciones solo se admiten en el estado AArch64.
Actualmente, los siguientes procesadores Arm64 Cortex-A admiten extensiones ARMv8.2:
- Corteza-A55
- Corteza-A75
- Corteza-A76
Para obtener más detalles, consulte el Manual de referencia de la arquitectura Armv8.
Disposición de la memoria del kernel en Arm64
Con la extensión ARMv8.2 que agrega soporte para el espacio LVA (que solo está disponible cuando se ejecuta con un tamaño de página de 64 KB), la cantidad de descriptores se amplía en el primer nivel de traducción.
Las direcciones de usuario tienen los bits 63:48 configurados en 0, mientras que las direcciones del kernel tienen los mismos bits configurados en 1. La selección de TTBRx está dada por el bit 63 de la dirección virtual. El swapper_pg_dir
contiene solo asignaciones del kernel (globales), mientras que el usuario pgd
contiene solo asignaciones de usuario (no globales). El swapper_pg_dir
la dirección se escribe en TTBR1 y nunca se escribe en TTBR0.
Diseño de memoria AArch64 Linux con páginas de 64 KB más tres niveles (52 bits con soporte de 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]
Búsqueda de tabla de traducción con páginas de 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
Búsqueda de tabla de traducción con páginas de 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
Soporte de VA de 52 bits en el kernel
Dado que los núcleos más nuevos con compatibilidad con LVA deberían funcionar bien en CPU más antiguas (que no admiten la extensión LVA en el hardware) y las CPU más nuevas (que admiten la extensión LVA en el hardware), el enfoque de diseño elegido es tener un solo binario que admita 52 bits (y debe poder retroceder a 48 bits al inicio temprano si la función de hardware no está presente). Es decir, el VMEMMAP debe tener un tamaño lo suficientemente grande para dispositivos virtuales de 52 bits y también debe tener un tamaño lo suficientemente grande para acomodar un PAGE_OFFSET
fijo .
Este enfoque de diseño requiere que el kernel admita las siguientes variables para el nuevo espacio de direcciones virtuales:
VA_BITS constant the *maximum* VA space size
vabits_actual variable the *actual* VA space size
Entonces, mientras VA_BITS
indica el tamaño máximo del espacio VA, el espacio VA real admitido (según el cambio realizado en el momento del arranque) se indica mediante vabits_actual
.
Volteando el diseño de la memoria del kernel
El enfoque de diseño de mantener un solo kernel binario requiere que el kernel .text esté en las direcciones más altas, de modo que no varíen con los VA de 48/52 bits. Debido a que la sombra de Kernel Address Sanitizer (KASAN) es una fracción de todo el espacio VA del kernel, el final de la sombra KASAN también debe estar en la mitad superior del espacio VA del kernel para 48 y 52 bits. (Al cambiar de 48 bits a 52 bits, el final de la sombra KASAN es invariable y depende de ~0UL
, mientras que la dirección de inicio "crecerá" hacia las direcciones inferiores).
Para optimizar phys_to_virt()
y virt_to_phys()
, el PAGE_OFFSET
se mantiene constante en 0xFFF0000000000000
(correspondiente a 52 bits), esto evita la necesidad de una lectura de variable adicional. El physvirt
y vmemmap
las compensaciones se calculan en el inicio temprano para habilitar esta lógica.
Considere la siguiente conversión de espacio de direcciones de RAM física frente a virtual:
/*
* 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
Impacto en las aplicaciones del espacio de usuario utilizadas para depurar el kernel
Se utilizan varias aplicaciones de espacio de usuario para depurar kernels activos/en ejecución o analizar el volcado de vmcore de un sistema bloqueado (por ejemplo, para determinar la causa raíz del bloqueo del kernel):herramientas kexec, makedumpfile y crash-utility.
Cuando se utilizan para depurar el kernel de Arm64, también tienen un impacto debido a que el mapa de memoria del kernel de Arm64 se "invierte". Estas aplicaciones también necesitan realizar un recorrido por la tabla de traducción para determinar una dirección física correspondiente a una dirección virtual (similar a cómo se hace en el kernel).
En consecuencia, las aplicaciones del espacio de usuario deben modificarse ya que se rompen aguas arriba después de que se introdujo el "cambio" en el mapa de memoria del kernel.
He propuesto correcciones en las tres aplicaciones de espacio de usuario afectadas; mientras que algunos han sido aceptados aguas arriba, otros aún están pendientes:
- Propuesta de corrección upstream de makedumpfile
- Corrección ascendente propuesta de herramientas kexec
- Corrección aceptada en la utilidad de bloqueo
A menos que estos cambios se realicen en las aplicaciones del espacio de usuario, permanecerán dañados para depurar kernels activos o en ejecución o analizar el volcado de vmcore de un sistema bloqueado.
Vas de espacio de usuario de 52 bits
Para mantener la compatibilidad con las aplicaciones del espacio de usuario que se basan en el tamaño máximo de 48 bits del espacio VA de ARMv8.0, el kernel, de forma predeterminada, devolverá las direcciones virtuales al espacio de usuario desde un rango de 48 bits.
Las aplicaciones del espacio de usuario pueden "suscribirse" para recibir VA desde un espacio de 52 bits especificando un parámetro de sugerencia mmap mayor de 48 bits.
Por ejemplo:
.mmap_high_addr.c
----
maybe_high_address = mmap(~0UL, size, prot, flags,...);
También es posible crear un kernel de depuración que devuelva direcciones desde un espacio de 52 bits habilitando las siguientes opciones de configuración del kernel:
CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
Tenga en cuenta que esta opción solo está diseñada para depurar aplicaciones y no utilizarse en producción.
Conclusiones
Para resumir:
- A partir de la versión 5.14 del kernel de Linux, las nuevas extensiones de hardware Armv8.2 LVA y LPA ahora son compatibles con el kernel de Linux.
- Las aplicaciones de espacio de usuario como kexec-tools y makedumpfile utilizadas para depurar el kernel están rotas ahora mismo y en espera de la aceptación de las correcciones anteriores.
- Las aplicaciones de espacio de usuario heredadas que se basan en el kernel Arm64 que le proporciona un VA de 48 bits seguirán funcionando tal como están, mientras que las aplicaciones de espacio de usuario más nuevas pueden "optar" por recibir VA desde un espacio de 52 bits especificando un parámetro de sugerencia de mmap. que es más grande que 48 bits.
Este artículo se basa en el diseño de memoria en AArch64 Linux y en la documentación del kernel de Linux v5.9.12. Ambos tienen licencia GPLv2.0.