요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
==============================
Memory Layout on AArch64 Linux
==============================
Author: Catalin Marinas <[email protected]>
This document describes the virtual memory layout used by the AArch64
Linux kernel. The architecture allows up to 4 levels of translation
tables with a 4KB page size and up to 3 levels with a 64KB page size.
AArch64 Linux uses either 3 levels or 4 levels of translation tables
with the 4KB page configuration, allowing 39-bit (512GB) or 48-bit
(256TB) virtual addresses, respectively, for both user and kernel. With
64KB pages, only 2 levels of translation tables, allowing 42-bit (4TB)
virtual address, are used but the memory layout is the same.
ARMv8.2 adds optional support for Large Virtual Address space. This is
only available when running with a 64KB page size and expands the
number of descriptors in the first level of translation.
TTBRx selection is given by bit 55 of the virtual address. The
swapper_pg_dir contains only kernel (global) mappings while the user pgd
contains only user (non-global) mappings. The swapper_pg_dir address is
written to TTBR1 and never written to TTBR0.
When using KVM without the Virtualization Host Extensions, the
hypervisor maps kernel pages in EL2 at a fixed (and potentially
random) offset from the linear mapping. See the kern_hyp_va macro and
kvm_update_va_mask function for more details. MMIO devices such as
GICv2 gets mapped next to the HYP idmap page, as do vectors when
ARM64_SPECTRE_V3A is enabled for particular CPUs.
When using KVM with the Virtualization Host Extensions, no additional
mappings are created, since the host kernel runs directly in EL2.
52-bit VA support in the kernel
-------------------------------
If the ARMv8.2-LVA optional feature is present, and we are running
with a 64KB page size; then it is possible to use 52-bits of address
space for both userspace and kernel addresses. However, any kernel
binary that supports 52-bit must also be able to fall back to 48-bit
at early boot time if the hardware feature is not present.
This fallback mechanism necessitates the kernel .text to be in the
higher addresses such that they are invariant to 48/52-bit VAs. Due
to the kasan shadow being a fraction of the entire kernel VA space,
the end of the kasan shadow must also be in the higher half of the
kernel VA space for both 48/52-bit. (Switching from 48-bit to 52-bit,
the end of the kasan shadow is invariant and dependent on ~0UL,
whilst the start address will "grow" towards the lower addresses).
In order to optimise phys_to_virt and virt_to_phys, the PAGE_OFFSET
is kept constant at 0xFFF0000000000000 (corresponding to 52-bit),
this obviates the need for an extra variable read. The physvirt
offset and vmemmap offsets are computed at early boot to enable
this logic.
As a single binary will need to support both 48-bit and 52-bit VA
spaces, the VMEMMAP must be sized large enough for 52-bit VAs and
also must be sized large enough to accommodate a fixed PAGE_OFFSET.
Most code in the kernel should not need to consider the VA_BITS, for
code that does need to know the VA size the variables are
defined as follows:
VA_BITS constant the *maximum* VA space size
VA_BITS_MIN constant the *minimum* VA space size
vabits_actual variable the *actual* VA space size
Maximum and minimum sizes can be useful to ensure that buffers are
sized large enough or that addresses are positioned close enough for
the "worst" case.
52-bit userspace VAs
--------------------
To maintain compatibility with software that relies on the ARMv8.0
VA space maximum size of 48-bits, the kernel will, by default,
return virtual addresses to userspace from a 48-bit range.
Software can "opt-in" to receiving VAs from a 52-bit space by
specifying an mmap hint parameter that is larger than 48-bit.
For example:
.. code-block:: c
maybe_high_address = mmap(~0UL, size, prot, flags,...);
It is also possible to build a debug kernel that returns addresses
from a 52-bit space by enabling the following kernel config options:
.. code-block:: sh
CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
Note that this option is only intended for debugging applications
and should not be used in production.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
AArch64 가상 메모리 배치
1-35저자: Catalin Marinas <[email protected]>. 이 문서는 AArch64 Linux 커널이 사용하는 가상 메모리 배치를 설명합니다. 아키텍처는 4KB 페이지에서 최대 4단계, 64KB 페이지에서 최대 3단계의 변환 테이블을 허용합니다.
Linux는 4KB 페이지 구성에서 3단계 또는 4단계 변환 테이블을 사용합니다. 이에 따라 사용자 공간과 커널 공간 모두 각각 `39-bit`(512GB) 또는 `48-bit`(256TB) 가상 주소를 사용할 수 있습니다. 64KB 페이지에서는 2단계 변환 테이블만 사용해 42비트(4TB) 가상 주소를 제공하지만 메모리 배치 자체는 같습니다.
ARMv8.2의 선택 기능인 LVA(Large Virtual Address)는 64KB 페이지에서만 사용할 수 있으며 첫 번째 변환 단계의 descriptor 수를 늘립니다.
가상 주소의 55번 비트가 `TTBRx` 선택을 결정합니다. `swapper_pg_dir`에는 커널의 global mapping만, 사용자 `pgd`에는 사용자의 non-global mapping만 들어갑니다. `swapper_pg_dir` 주소는 `TTBR1`에 기록되며 `TTBR0`에는 절대 기록되지 않습니다.
VHE 없이 KVM을 사용할 때 hypervisor는 linear mapping에서 고정된, 경우에 따라 무작위인 offset을 두고 EL2에 커널 페이지를 매핑합니다. 자세한 내용은 `kern_hyp_va` macro와 `kvm_update_va_mask()`를 참조하십시오. GICv2 같은 MMIO 장치는 HYP identity-map page 옆에 매핑되고, 특정 CPU에서 `ARM64_SPECTRE_V3A`가 켜졌다면 vector도 같은 방식으로 배치됩니다. VHE를 사용하면 host kernel이 EL2에서 직접 실행되므로 추가 mapping을 만들지 않습니다.
커널의 52비트 VA 지원
36-76ARMv8.2-LVA 선택 기능이 있고 64KB 페이지로 실행 중이면 사용자 공간과 커널 주소에 `52-bit` 주소 공간을 사용할 수 있습니다. 다만 52비트를 지원하는 커널 binary는 하드웨어 기능이 없을 때 초기 boot 단계에서 48비트로 되돌아갈 수 있어야 합니다.
이 fallback 때문에 커널 `.text`는 48비트와 52비트 VA에서 변하지 않는 높은 주소에 놓입니다. KASAN shadow는 전체 커널 VA 공간의 일정 비율이므로 shadow의 끝도 두 구성 모두에서 커널 VA 공간의 높은 절반에 있어야 합니다. 48비트에서 52비트로 전환하면 `~0UL`에 의존하는 끝 주소는 그대로이고 시작 주소가 더 낮은 주소 쪽으로 확장됩니다.
`phys_to_virt()`와 `virt_to_phys()`를 최적화하기 위해 `PAGE_OFFSET`은 52비트에 해당하는 `0xFFF0000000000000`으로 고정합니다. 추가 변수 읽기가 필요하지 않으며, 이 동작에 필요한 physvirt offset과 vmemmap offset은 초기 boot에서 계산합니다.
단일 binary가 48비트와 52비트 VA 공간을 모두 지원해야 하므로 `VMEMMAP`은 52비트 VA와 고정 `PAGE_OFFSET`을 모두 수용할 만큼 크게 잡아야 합니다. 대부분의 커널 코드는 `VA_BITS`를 의식할 필요가 없습니다. VA 크기가 필요한 코드는 다음 값을 사용합니다.
| 이름 | 형태 | 의미 |
|---|---|---|
| `VA_BITS` | constant | 지원하는 최대 VA 공간 크기 |
| `VA_BITS_MIN` | constant | 지원하는 최소 VA 공간 크기 |
| `vabits_actual` | variable | 실제로 선택된 VA 공간 크기 |
최대값과 최소값은 최악의 경우에도 buffer가 충분히 크거나 주소가 필요한 거리 안에 놓이는지 확인할 때 유용합니다.
사용자 공간의 52비트 VA
77-100ARMv8.0의 최대 48비트 VA 공간에 의존하는 software와 호환되도록 커널은 기본적으로 48비트 범위에서 사용자 공간 가상 주소를 반환합니다.
Software는 48비트보다 큰 `mmap()` hint를 지정해 52비트 공간의 VA를 받도록 명시적으로 선택할 수 있습니다.
maybe_high_address = mmap(~0UL, size, prot, flags,...);
다음 kernel config option을 켜면 52비트 공간의 주소를 반환하는 debug kernel도 만들 수 있습니다.
CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y
`CONFIG_ARM64_FORCE_52BIT`는 application debugging 전용이며 production 환경에서 사용하면 안 됩니다.
요약과 해설
memory.rst:1-100AArch64 Linux는 page size와 변환 단계 수에 따라 39비트부터 52비트까지 VA 공간을 구성합니다. 핵심은 user/kernel mapping을 `TTBR0`/`TTBR1`으로 분리하면서 하나의 kernel binary가 48비트와 52비트 hardware를 모두 지원하도록 높은 주소 영역을 안정적으로 유지하는 것입니다.
Page size와 hardware LVA 지원이 kernel이 사용할 수 있는 주소 폭을 결정합니다.
TTBR과 VA 관련 값이 담당하는 역할을 구분합니다.