페이지 테이블 – ARM32

리눅스의 4레벨 페이지 테이블 관리

리눅스는 32비트 및 64비트 등 모든 아키텍처를 지원하기 위해 최대 4레벨 변환을 사용

  • 64비트 리눅스 커널을 대비하기 위하여 4 레벨의 테이블로 관리할 수 있게 확장하였다.
    (pgd → pud → pmd → pte 순서)
  • 커널 v4.12를 목표로 x86 시스템을 위해 128PB 용량이 지원되는 5 레벨 페이지 테이블을 준비하기 시작하였다.

pgd-2

pgd-3

 

ARM32 리눅스의 3 레벨 페이지 테이블 관리

ARM32 리눅스는 3 레벨의 페이지 테이블을 관리한다. 그러나 하드웨어 레벨에서는 LPAE를 사용하지 않는 경우 ARM32는 2 레벨의 페이지 테이블만을 운용하고, LPAE를 사용하는 경우에만 3레벨 페이지 테이블을 사용한다.

  • 속성 비트가 리눅스와 ARM h/w가 서로 일부 지원되지 않는 비트가 있어서 pte 테이블의 경우 linux 페이지 테이블과 hw 페이지 테이블로 나누어서 동시에 유지 관리를 하며 사용한다.
  • 대용량의 메모리를 지원하는 LPAE 옵션에 따라 다른 관리 방법을 사용한다.
    • LPAE를 지원하는 경우 리눅스나 ARM h/w 모두 3 레벨의 페이지 테이블 관리를 그대로 사용한다.
    • LPAE를 지원하지 않는 일반적인 경우에는 리눅스는 3 레벨 페이지 테이블 관리를 사용하고 ARM h/w는 2 레벨 페이지 테이블 관리를 사용해야 하므로 별도의 관리 방법을 사용하여 구현되었다.

 

다음 그림과 같이 LPAE를 사용하지 않는 경우 pmd 테이블은 pgd와 동일하다. 실제 4K 바이트로 구성된 PTE 테이블은 매우 독특하게 구성된다. PTE 테이블은 리눅스용 PTE 테이블 1 개와 h/w용 PTE 테이블 2 개가 기록되어 운용된다.

  • 파란 색 점선이 실제 물리주소가 담겨 연결된다.
  • 붉은 색 점선의 연결은 실제 값으로 연결된 상태가 아니라 로지컬하게 연결된 상태이다.

pgd-4 pgd-5

 

리눅스 3레벨 테이블 vs ARM32 h/w 2레벨 테이블

LPAE를 사용하지 않아 하드웨어 2 레벨 페이지 테이블을 사용하는 경우 리눅스의 3 레벨 테이블과 어떻게 매치되는가 알아본다.

 

테이블 레벨별 특징

  • pgd (16k)
    • 리눅스의 0 레벨 관리 테이블이며 하드웨어 레벨 관리 테이블과 동일하게 관리한다.
    • 리눅스는 2048개의 8 바이트 엔트리로  구성된다.
    • 하드웨어 레벨에서는 4096개의 4바이트로 인식하므로 각 pgd 엔트리는 pair를 이룬 4바이트 엔트리로 구성해야 한다.
  • pmd (16k)
    • 리눅스의 1 레벨 관리 테이블이지만 물리적으로는 pgd 테이블을 그대로 사용한다.
    • pgd의 2048개 엔트리를 4096개의 4바이트 엔트리로 구성된다.
      • pmd 테이블은 페이지의 실제 할당 없이 pgd의 테이블을 pmd 테이블로 실제 존재하는 것처럼 에뮬레이션 하여 사용한다.
      • pgd 테이블의 실제 내용 변경없이 호출 사이즈 구성(캐스트)만 바꿔 사용한다.
        • pgd 엔트리에 있는 pair를 이룬 4바이트 엔트리가 pmd 엔트리로 활용된다.
        • pair를 이룬 pmd 엔트리는 pair를 이룬 hw pte 엔트리를 대칭되게 가리킨다.
  • pte (4K)
    • 내부적으로 리눅스의 2 레벨 관리 테이블 1개와 ARM h/w 1 레벨 관리 테이블 2 개로 구성된다.
    • h/w pt는 256개의 4바이트 hw pte 엔트리로 구성된다.
    • 리눅스 pt는 512개의 4바이트 linux pte 엔트리로 구성된다.
    • 2 개의 hw/pt가 연달아 구성되고 그 밑에 linux pt가 구성된다.

 

다음 그림은 2 레벨로 연결된 페이지 테이블로 8바이트인 하나의 엔트리가 4바이트씩 두 개로 나뉘어 두 개의 페이지 테이블에 페어로 연결되는 모습을 보여준다.

pt-6

 

리눅스 페이지 변환 (32bit address)

pgd 엔트리가 2048개이며, pgd 엔트리 하나는 pmd 엔트리 2개로 구성된다.

pt-8

 

ARM H/W 페이지 변환 (32bit address)

pmd 엔트리가 4096개이며, 두 개의 pdm 엔트리가 쌍으로 사용되어 pgd 엔트리 하나에 대응된다.

  • 실제로 pgd와 pmd가 같은 테이블이다.

pt-7

 

엔트리 속성

pmd_t for small page

attr-1

  • page table base address
    • small page의 위치를 지정한다.

 

pmd_t for section page

attr-2

  • section base address
    • 곧장 1M page 프레임을 가리킨다.

 

pte_t for linux

attr-3

  • small page base address
    • 4K 페이지 프레임을 가리킨다.
  • L_PTE_PRESENT 또는 L_PTE_VALID (bit0)
    • 페이지가 메모리에 상주해 있고 스왑 아웃되지 않은 상태
  • L_PTE_YOUNG (bit1)
    • 페이지에 접근이 된 경우
  • L_PTE_DIRTY (bit6)
    • 페이지가 변경된 경우 설정되며 나중에 매핑된 파일에 기록되어야 함을 나타낸다.
  • L_PTE_RDONLY (bit7)
    • 읽기 전용 (1=read, 0=read/write)
  • L_PTE_USER (bit8)
    • user process도 접근 가능한 경우 1, 커널만 접근 가능하게 할 경우 0
  • L_PTE_XN (bit9)
    • Excute Never로 실행 금지
  • L_PTE_SHARED (bit10)
    • 공유된 페이지
  • L_PTE_NONE (bit11)
    • 페이지가 있으나 access 할 수 없는 페이지
    • NUMA 시스템에서 해당 페이지를 읽을 때 accesss 권한 실패로 인해 abort exception이 발생되어 fault된 후 해당 페이지를 사용하는 태스크의 migration을 고려하는 Automatic NUMA balancing을 위해 사용된다.

 

pte_t for ARM h/w

ARM32 PTE용 속성은 리눅스 속성과 약간 다르다.

attr-4

  • small page base address
    • 4K  페이지 프레임을 가리킨다.

 

ARM 레퍼런스 매뉴얼 참고

Short-descriptor translation table format

pt1

 

TTBCR(페이지 테이블 크기 결정 레지스터)

pt2

1차 페이지 테이블 엔트리 구성

pt3

2차 페이지 테이블 엔트리 구성

pt4

가상 주소 vs 물리 주소 변환

pt5

ARM32 vs ARM64 리눅스에서의 페이지 테이블 운영 비교

ARM32

32bit ARM 리눅스의 경우 유저용 페이지 테이블을 가리키는 TTBR0 레지스터를 사용한다. 이 레지스터가 각각의 유저 태스크의 pgd 페이지 테이블 간 스위칭을 한다. 중요한 것은 pgd 테이블은 커널 영역과 유저 영역을 모두 포함한다. 단 커널 영역의 관리는 컴파일 타임에 static 하게 생성된 별도의 pgd 페이지 테이블(&swapper_pg_dir)을 사용하여 유지 보수를 한다. 이렇게 유지 보수가 된 경우 각 사용자 테이블의 커널 영역을 담당하는 엔트리들로 모두 복사한다. 커널 페이지 테이블 엔트리가 갱신되면 시퀀스 카운터만 증가시키고, 각 태스크가 VM 스위칭될 때 자신의 커널 영역의 페이지 엔트리들이 갱신되어야 할 지 여부를 이 시퀀스 카운터를 비교하여 복사한다.

 

다음 그림은 ARM32에서 각각 3G 크기의 유저 영역을 갖는 2 개의 태스크가 동작할 때 운용되는 페이지 테이블의 모습을 보여준다.

  • TTBR1은 커널을 담당하는 swapper_pg_dir 라는 이름의 pgd 페이지 테이블을 사용한다.
  • TTBR0은 유저 태스크마다 생성되는 해당 유저 태스크의 pgd 페이지 테이블을 가리키지만 이 곳에서 커널 영역도 같이 운용된다.
    • 인터럽트가 발생할 때 TTBR1으로 전환하지 않고, 그냥 TTBR0를 사용한다. 이렇게 전환해야 copy_from_user() 및 copy_to_user() 함수를 사용할 때 커널은 커널과 유저 영역을 동시에 접근할 수 있다.

ARM64

ARM32에서 하나의 pgd 유저 페이지 테이블이 커널과 유저 영역을 통합하여 사용하고, 커널 엔트리들을 복사하는 등 복잡하게 운용하는 것과 달리 ARM64의 경우 간단히 커널과 유저용 페이지 테이블을 별도로 운용한다.

 

다음 그림은 ARM64에서 각각 256T 크기의 유저 영역을 갖는 2 개의 태스크가 동작할 때 운용되는 페이지 테이블의 모습을 보여준다.

  • 커널과 유저 영역의 사용 및 관리를 완전 별도의 TTBR0와 TTBR1이 따로 관리하고, 페이지 테이블 pgd도 별도로 운영한다.
    • 커널용 pgd 페이지 테이블은 컴파일 타임에 static하게 생성된 swapper_pg_dir을 사용한다.

 

참고

6 thoughts to “페이지 테이블 – ARM32”

  1. 안녕하세요 페이지 매핑 관련해서 질문이 있어 이렇게 글을 올립니다.

    pte 테이블의 경우 linux 페이지 테이블과 hw 페이지 테이블 2가지가 있다고 했는데 이게 정확히 뭘 의미하는지 잘 모르겠습니다.

    리눅스 페이지 테이블은 mmu가 없고 hw페이지 테이블은 있다는 걸 의미하는 건가요?

    만약 그렇다면 mmu가 있는 hw테이블은 실제 pte를 위한 메모리 할당이 필요 없는건가요??

    1. 안녕하세요?

      arm32 pte 테이블이 복잡해서 혼동되시죠?

      마지막 단계의 pte 페이지 테이블의 엔트리가 매핑된 물리 페이지를 가리키잖아요?
      해당 엔트리에는 물리 페이지 주소외에도 매핑 관련 속성 비트들이 필요합니다.

      그런데 arm32의 경우 pte 페이지 테이블의 엔트리에 리눅스가 사용하는 몇 개의 관리용 속성 비트가 빠져있고, 32비트 엔트리라 비어 있는 비트도 없어서 관리 비트의 저장이 불가능한 상태입니다. 그래서 arm32 커널은 할당된 페이지 테이블을 반으로 잘라 실제 MMU가 사용하는 하드웨어 페이지 테이블과 리눅스가 관리하는 페이지 테이블 둘로 나누어 관리하는 것입니다.

      결국 arm32에서는 MMU가 실제로 사용하는 pte 페이지 테이블은 hw 페이지 테이블입니다.

      반면에 상위 단계의 페이지 테이블은 hw 페이지 테이블과 리눅스 페이지 테이블로 나누지 않고 그냥 사용합니다.

      마지막 할당 질문은 위에서 말한 바와 같이 pte 페이지 테이블용으로 4K 페이지를 할당 받아 반으로 나누어 사용합니다.

      감사합니다.

      1. 답변 감사합니다. 이해가 잘 됩니다 ㅎㅎ

        한가지 더 질문드릴게.. 극초반에.. 그니까 paging_init()하기 전에도 pgd테이블영역을 사용해서 간접주소방식을 사용하던데
        그때는 pte테이블을 추가로 할당받지 않고 그냥 pgd테이블에다가 바로 물리 주소를 매핑해서 사용하더라고요
        그래서 이번에 pte테이블을 이해하는데 좀 힘들었었는데 여기서 질문은 pgd테이블에 바로 매핑해서 사용해도 되는거고
        pte를 추가로 할당해서 그 영역에 매핑해서 사용할 수도 있는건가요?? MMU 레지스터를 어떻게 세팅하냐에 따라 달라지는 건가요??
        (아직 mmu에 대한 자세한 동작 방식은 공부를 안해서 잘 모릅니다;;)

        감사합니다.

  2. 4G 이하의 물리 주소를 가지는 대부분의 ARM32 시스템의 경우 페이지 테이블은 2개를 사용합니다. pgd 테이블과 pte 테이블입니다.
    (참고로 4G 이상 물리 공간을 사용할 수 있는 ARM with LPAE는 3 단계 페이지 테이블을 사용하고, 일부 ARM32 시스템에서 잠깐 사용되었습니다.)

    pgd 테이블 엔트리 하나는 8바이트 이지만 내부적으로는 4 바이트 엔트리 2 개 페어로 사용됩니다.
    4 바이트 엔트리당 1M 공간에 대한 가상 주소 -> 물리 주소 변환을 담당합니다.
    pgd 엔트리가 1M를 담당해야 하므로 4K 페이지 매핑은 불가능합니다. 그러나 특수한 상황이 하나 있습니다.
    4K 페이지가 1M 주소 단위로 연속적으로 붙어 있는 경우, 즉 1M 단위로 정렬된 경우 페이지 매핑이 아닌 섹션 매핑을 할 수 있습니다. 즉 pgd 테이블에서 1M 단위의 섹션 블럭을 매핑할 수 있습니다.

    결국 4K 페이지 하나를 매핑하려면 pte 테이블에서 해야 합니다.

    또 하나 이해하셔야 하는 것은 4K 페이지를 매핑하기 위해 __create_pgd_mapping() 함수같은 것을 사용하면, 보통 필요로 하는 단계의 테이블까지 알아서 순서대로 내려 가서 매핑합니다.

    감사합니다.

  3. 그렇군요.. 저는 pte에 모두 다 매핑할줄 알았는데 보니까 1M단위가 넘어갈경우엔 그냥 pgd에다가 관리를 하는건가 보네요

    명쾌한 답변 감사합니다 ~!!

댓글 남기기