ARM64에서의 커널 메모리 맵 🔗
<코드로 알아보는 ARM 리눅스 커널> 에서의 “페이징과 매핑"이라는 챕터를 읽으면서 커널 메모리 맵에 대한 간략한 설명을 포스팅으로 정리하고자 한다. 물리 메모리와 가상 메모리 주소간의 매핑을 위해 사용하는 테이블을 매핑 테이블이라고 하며, 본 포스팅에서는 기본적인 개념인 가상 주소 공간과 ARM64에서의 커널 메모리 맵 구성을 정리한다.
ARM64 커널에서는 64비트 가상 주소의 시작 부분과 끝부분의 영역을 사용한다. 이 때, 가상 주소 기준으로 각 끝 영역을 사용한다는 점에 유의해야 한다.
가상 주소 공간 🔗
ARM64 커널에서는 64비트 가상 주소의 시작 부분과 끝부분의 가상 주소 공간을 사용한다. 각각의 공간은 서로 다른 TTBR(Translation Table Base Register)를 사용하기 때문에 각자의 페이지 테이블을 갖고 있다. 이 때, 커널 주소 공간은 TTBR1, 사용자 주소 공간은 TTBR0를 사용하며 각각의 레지스터는 MMU 디바이스 내부에 저장된다.
페이지 테이블을 엔트리 여러 개로 구성되며, 페이지 테이블 엔트리 하나는 페이지 1개에 대한 물리주소로 변환하는데 사용한다. ARM64 커널에서는 페이지 크기로 4KB, 16KB, 64KB 중 하나를 사용할 수 있다(기본 4KB). 중요한 점은 ARM64 커널이 64비트 가상 주소 전체를 사용하는 것이 아니라 36, 39, 42, 47 및 최대 48비트와 같이 아키텍처가 지원하는 비트만을 선택해 사용할 수 있고 이 범위를 가상 주소로 사용한다. 이러한 비트는 VA_BITS
값으로 설정하며 페이지 크기와 페이지 테이블 엔트리 인덱스, 페이지 오프셋 크기 구성에 따라 페이지 테이블 엔트리의 수가 결정된다.
커널에서 레벨에 따라 페이지 테이블 명칭은 아래와 같이 구분한다.
- 1단계: PGD (Page Global Directory)
- 2단계: PUD (Page Upper Directory)
- 3단계: PMD (Page Mid-level Directory)
- 4단계: PTE (Page Table Entry)
예를 들어, VA_BITS 를 36으로 설정한다면, 아래와 같이 64비트 가상주소 중에서 테이블 엔트리 인덱스와 페이지 오프셋을 포함한 비트가 36비트를 차지한다. 그리고 2단계 페이지 테이블을 구성한다면, 16K 페이지를 사용한다고 가정했을 때 아래와 같이 가상주소의 구성을 생각할 수 있다.
ARM64 커널의 기본 구성인 3단계 페이지 테이블 🔗
커널의 기본 구성인 4K 페이지와 VA_BITS = 39
로 구성된 3단계 페이지 테이블을 사용하는 경우를 살펴보자. 64비트 중 39비트에 포함하지 않는 상위 25비트는 커널(모두 1)과 유저 주소 공간(모두 0) 구분에 사용하고, 나머지 39비트를 각각 9비트/9비트/9비트/12비트(페이지 오프셋용) 으로 나누어 사용한다. 상위 비트에 따라 TTBR을 어떤 것을 사용할지를 결정한다.
ARM64 커널 메모리 맵 🔗
커널용 가상 주소 영역도 독특하게 사용한다. 상위 절반은 물리 메모리를 1:1로 미리 매핑하여 사용하며 나머지 절반에 대해 fixmap
, vmalloc
, vmemmap
, pci-iomap
등의 영역과 커널 이미지 영역으로 나누어 사용한다.
- fixmap: 컴파일 테임에 목적에 따라 가상 주소 공간이 이미 결정된 매핑 영역이다.
- vmalloc: 런타임에 연속된 가상 주소 공간을 자유롭게 매핑할 수 있는 영역이다.
vmalloc()
함수가vmap()
함수를 통해 이용하는 곳이고ioremap()
역시 사용하는 영역이다. - vmemmap: 빠른 접근을 위해 분산된 페이지 구조체들을 이 영역에 매핑하기 위해 사용한다.
- pci-iomap: PCI 디바이스의 메모리 맵 I/O 영역으로 사용한다. PCI 디바이스만 사용하며, 일반적으로 ioremap 함수를 통해 매핑하는 곳은 vmalloc 영역이다.