01 · QUESTION
무엇을 확인할 것인가
pte_present()가 참이라는 공통 결과 뒤에서 hardware page walker가 실제로 검사하는 bit와 permission은 어떻게 다른가?
mm core는 pgd/p4d/pud/pmd/pte API를 사용하지만 folded level, leaf 허용 level, accessed/dirty hardware 지원과 execute permission encoding은 architecture가 정한다.
software PTE helper가 만든 raw value가 memory에 publish되고 TLB invalidate가 끝난 뒤에야 hardware permission이 바뀐다. PTE write와 shootdown을 하나의 transaction으로 읽는다.
02 · CONTRACT
공통 계약과 architecture 구현
| architecture | 핵심 mechanism | 실패 형태 | 확인할 상태 |
|---|---|---|---|
| arm64 | valid/table bit와 AttrIndx, AP, SH, AF, UXN/PXN descriptor | table descriptor를 block으로 잘못 해석하거나 BBM을 어기면 stale translation 또는 architecture fault가 난다. | raw PTE, level, output PA, AttrIndx, AP, SH, AF, UXN/PXN과 granule을 출력한다. |
| x86-64 | Present/RW/US/PWT/PCD/A/D/PS/NX와 software bit | NXE, PAT, encryption C-bit 또는 software protnone encoding을 누락하면 permission과 cache type이 달라진다. | CR4.LA57, EFER.NXE, raw PTE, PS/G/NX와 physical-address mask를 확인한다. |
| RISC-V | V/R/W/X/U/G/A/D와 PPN field | W=1,R=0 같은 reserved encoding이나 misaligned superpage PPN은 page fault를 만든다. | SATP.MODE, ASID, level index, V/R/W/X/U/G/A/D와 PPN 정렬을 확인한다. |
03 · DIAGRAMS
세 그림으로 먼저 읽기
arm64
- mechanism
- valid/table bit와 AttrIndx, AP, SH, AF, UXN/PXN descriptor
- state
- 4KiB granule 기준 최대 4/5 level을 사용하며 block descriptor가 PUD/PMD leaf를 만든다. UXN과 PXN이 user/kernel execute를 분리하고 AF/DBM이 access/dirty 관리에 관여한다.
- checkpoint
- raw PTE, level, output PA, AttrIndx, AP, SH, AF, UXN/PXN과 granule을 출력한다.
x86-64
- mechanism
- Present/RW/US/PWT/PCD/A/D/PS/NX와 software bit
- state
- 4-level 또는 LA57 5-level이며 PMD/PUD의 PS bit가 large page leaf를 표시한다. PAT bit 위치는 4KiB와 huge PTE에서 달라지고 global bit는 CR3 switch 생존 범위에 영향을 준다.
- checkpoint
- CR4.LA57, EFER.NXE, raw PTE, PS/G/NX와 physical-address mask를 확인한다.
RISC-V
- mechanism
- V/R/W/X/U/G/A/D와 PPN field
- state
- R/W/X가 모두 0이면 next-level table이고 하나라도 설정되면 leaf다. Sv39, Sv48, Sv57 mode에 따라 root level이 달라지며 superpage leaf는 낮은 PPN field 정렬을 요구한다.
- checkpoint
- SATP.MODE, ASID, level index, V/R/W/X/U/G/A/D와 PPN 정렬을 확인한다.
pgd/p4d/pud/pmd/pte API를 사용하지만 folded level, leaf 허용 level, accessed/dirty hardware 지원과 execute permission encoding은 architecture가 정한다.sfence.vma가 old translation을 제거해야 한다.sfence.vma가 old translation을 제거해야 한다.→관찰: SATP.MODE, ASID, level index, V/R/W/X/U/G/A/D와 PPN 정렬을 확인한다.04 · SOURCE
Linux 6.18.37 원본 코드와 주요 구문 해설
소스 위치를 고정된 숫자로 복사하지 않고 Linux v6.18.37 tree에서 함수 선언을 다시 찾아 발췌했습니다. 원문 전체 발췌를 보존하고, 개별 해설은 근거가 있는 구문에만 붙였습니다.
arm64 · Linux 6.18.37
valid/table bit와 AttrIndx, AP, SH, AF, UXN/PXN descriptor
4KiB granule 기준 최대 4/5 level을 사용하며 block descriptor가 PUD/PMD leaf를 만든다. UXN과 PXN이 user/kernel execute를 분리하고 AF/DBM이 access/dirty 관리에 관여한다.
원본 코드: arch/arm64/include/asm/pgtable-hwdef.h:156-214
156 * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
157 */
158#define PMD_ATTRINDX(t) (_AT(pmdval_t, (t)) << 2)
159#define PMD_ATTRINDX_MASK (_AT(pmdval_t, 7) << 2)
160
161/*
162 * Level 3 descriptor (PTE).
163 */
164#define PTE_VALID (_AT(pteval_t, 1) << 0)
165#define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0)
166#define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0)
167#define PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */
168#define PTE_RDONLY (_AT(pteval_t, 1) << 7) /* AP[2] */
169#define PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */
170#define PTE_AF (_AT(pteval_t, 1) << 10) /* Access Flag */
171#define PTE_NG (_AT(pteval_t, 1) << 11) /* nG */
172#define PTE_GP (_AT(pteval_t, 1) << 50) /* BTI guarded */
173#define PTE_DBM (_AT(pteval_t, 1) << 51) /* Dirty Bit Management */
174#define PTE_CONT (_AT(pteval_t, 1) << 52) /* Contiguous range */
175#define PTE_PXN (_AT(pteval_t, 1) << 53) /* Privileged XN */
176#define PTE_UXN (_AT(pteval_t, 1) << 54) /* User XN */
177#define PTE_SWBITS_MASK _AT(pteval_t, (BIT(63) | GENMASK(58, 55)))
178
179#define PTE_ADDR_LOW (((_AT(pteval_t, 1) << (50 - PAGE_SHIFT)) - 1) << PAGE_SHIFT)
180#ifdef CONFIG_ARM64_PA_BITS_52
181#ifdef CONFIG_ARM64_64K_PAGES
182#define PTE_ADDR_HIGH (_AT(pteval_t, 0xf) << 12)
183#define PTE_ADDR_HIGH_SHIFT 36
184#define PHYS_TO_PTE_ADDR_MASK (PTE_ADDR_LOW | PTE_ADDR_HIGH)
185#else
186#define PTE_ADDR_HIGH (_AT(pteval_t, 0x3) << 8)
187#define PTE_ADDR_HIGH_SHIFT 42
188#define PHYS_TO_PTE_ADDR_MASK GENMASK_ULL(49, 8)
189#endif
190#endif
191
192/*
193 * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
194 */
195#define PTE_ATTRINDX(t) (_AT(pteval_t, (t)) << 2)
196#define PTE_ATTRINDX_MASK (_AT(pteval_t, 7) << 2)
197
198/*
199 * PIIndex[3:0] encoding (Permission Indirection Extension)
200 */
201#define PTE_PI_IDX_0 6 /* AP[1], USER */
202#define PTE_PI_IDX_1 51 /* DBM */
203#define PTE_PI_IDX_2 53 /* PXN */
204#define PTE_PI_IDX_3 54 /* UXN */
205
206/*
207 * POIndex[2:0] encoding (Permission Overlay Extension)
208 */
209#define PTE_PO_IDX_0 (_AT(pteval_t, 1) << 60)
210#define PTE_PO_IDX_1 (_AT(pteval_t, 1) << 61)
211#define PTE_PO_IDX_2 (_AT(pteval_t, 1) << 62)
212
213#define PTE_PO_IDX_MASK GENMASK_ULL(62, 60)
214 주요 구문 해설
원본 59줄은 그대로 표시하며, 검토한 38개 구문에 설명을 붙였습니다.
#define PMD_ATTRINDX(t) (_AT(pmdval_t, (t)) << 2)メモリ属性番号 tをPMD記述子のビット[4:2]へ置きます。ここに入るのはキャッシュ属性そのものではなくMAIRレジスタ内の属性を選ぶ3ビットの番号です。_ATでPMD値の型に合わせてからずらします。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PMD_ATTRINDX_MASK (_AT(pmdval_t, 7) << 2)PMDのAttrIndxに対応するビット[4:2]だけが1のマスクです。7を2ビット左へずらして0x1cを作り、古い属性番号を取り出す、またはその部分だけ消して置き換える操作に使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_VALID (_AT(pteval_t, 1) << 0)PTEの有効ビット0を表す値1です。このビットを立てた記述子をハードウェアが有効な変換情報として扱い、クリアした記述子はそのままの有効マッピングとしては使えません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0)PTEの下位2ビットを取り出すマスク3です。有効ビットだけでなく記述子の形式を一緒に調べるために使い、アドレス部分やアクセス権にはかかりません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0)レベル3でページへのマッピングを表す下位2ビットの値0b11です。PTE_VALIDだけを立てるのではなくレベル3のページ記述子として正しい形式を作るために使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */通常の権限形式でEL0からのアクセスを許可するAP[1]、ビット6のマスクです。読み書きの最終可否は読み取り専用ビットや他の制御にも依存します。Permission Indirection使用時は同じ位置が権限表の索引に使われます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_RDONLY (_AT(pteval_t, 1) << 7) /* AP[2] */通常の権限形式のAP[2]、ビット7です。立っているとハードウェアは読み取り専用として扱います。Dirty Bit Managementを使う場合は更新可能なマッピングにも初めは立つことがあり、ソフトウェア上の書き込み許可とは分けて判断します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */SH[1:0]に0b11を入れ、通常形式のページをinner-shareableとして扱う値です。LPA2が有効な形式ではこのビット位置に物理アドレス上位が入るため、実際の保護属性ではPTE_MAYBE_SHAREDを通して適用を切り替えます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_AF (_AT(pteval_t, 1) << 10) /* Access Flag */Access Flag가 clear인 valid mapping은 hardware access flag fault 또는 update 경로를 유발한다.
#define PTE_NG (_AT(pteval_t, 1) << 11) /* nG */ビット11のnG、non-global属性です。TLBでアドレス空間のASIDを区別する必要があるマッピングに使い、別のプロセスの同じ仮想アドレスと変換を混同しないようにします。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_GP (_AT(pteval_t, 1) << 50) /* BTI guarded */ビット50のGuarded Page属性です。BTIを使用する実行ページに付け、間接分岐が許された着地点へ入るかという検査につなげます。このビットだけで非対応CPUにBTI機能を追加するわけではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_DBM (_AT(pteval_t, 1) << 51) /* Dirty Bit Management */ビット51のDirty Bit Management属性です。ハードウェアが書き込み時に読み取り専用状態を更新できるマッピングを示すために使い、LinuxではPTE_WRITEの表現にも利用します。このビット自体が単純な書き込み済み印という意味ではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_CONT (_AT(pteval_t, 1) << 52) /* Contiguous range */ビット52のContiguousヒントです。必要な境界・連続性・共通属性を満たす隣接記述子の組を、TLBでまとめて扱えるようにします。これを立てるだけで物理ページを連続配置に変えるものではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PXN (_AT(pteval_t, 1) << 53) /* Privileged XN */ビット53のPXN、特権実行禁止属性です。通常の権限形式でこのページからEL1の命令を実行させないために使います。EL0の実行可否を表すUXNとは別の位置です。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_UXN (_AT(pteval_t, 1) << 54) /* User XN */user execute-never permission bit로 kernel PXN과 독립적이다.
#define PTE_SWBITS_MASK _AT(pteval_t, (BIT(63) | GENMASK(58, 55)))Linuxがソフトウェア用に使うビット63と[58:55]をまとめたマスクです。BITは1ビット、GENMASKは連続範囲を選びます。ハードウェアのアドレス・権限部分と区別してソフトウェアの状態を取り扱うために使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_ADDR_LOW (((_AT(pteval_t, 1) << (50 - PAGE_SHIFT)) - 1) << PAGE_SHIFT)PAGE_SHIFTから49までを1にしたアドレス抽出用マスクです。下位のページ内オフセットを除き、PTEに通常位置で入る物理アドレス部分を取り出します。52ビット物理アドレスの残りは下のPTE_ADDR_HIGHと別に合成します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#ifdef CONFIG_ARM64_PA_BITS_5252비트 물리 주소를 지원하는 빌드에서는 PTE의 일반 주소 필드에 들어가지 않는 상위 주소 비트의 별도 인코딩을 정의합니다. 가상 주소 폭을 선택하는 CONFIG_ARM64_VA_BITS_52와 다른 설정입니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#ifdef CONFIG_ARM64_64K_PAGES64KiB 페이지의 52비트 물리 주소 형식은 상위 네 비트를 PTE의 15:12에 담습니다. 다른 페이지 크기의 LPA2 형식에서는 상위 두 비트를 9:8에 담으므로 마스크와 이동량을 별도로 정의합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#define PTE_ADDR_HIGH (_AT(pteval_t, 0xf) << 12)52ビット物理アドレスと64KiBページの組み合わせで、PTEのビット[15:12]を選ぶマスクです。ここには物理アドレスの上位[51:48]をずらして格納するため、通常の連続したアドレスマスクだけでは復元できません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_ADDR_HIGH_SHIFT 3664KiBページの52ビット物理アドレスを復元するシフト量36です。PTE[15:12]を左へ36ビットずらして物理アドレス[51:48]へ戻し、逆変換では右へずらして埋め込みます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PHYS_TO_PTE_ADDR_MASK (PTE_ADDR_LOW | PTE_ADDR_HIGH)64KiBページの52ビット物理アドレスをPTEへ詰めたあと残す範囲です。通常位置のPTE_ADDR_LOWと、上位アドレスを移したPTE_ADDR_HIGHをORして、権限などに使う余分なビットを除きます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#else64KiB 페이지가 아닌 52비트 물리 주소 구성입니다. LPA2 형식에 맞춰 상위 두 주소 비트를 PTE의 9:8에서 처리하고 이동량과 추출 마스크도 그 형식으로 바꿉니다.
#define PTE_ADDR_HIGH (_AT(pteval_t, 0x3) << 8)52ビット物理アドレスを64KiB以外のページで扱う形式では、上位[51:50]をPTE[9:8]へ置きます。その2ビットを選ぶ値が0x3<<8です。LPA2で従来の共有属性位置をアドレスに使う点が64KiB形式と異なります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_ADDR_HIGH_SHIFT 42PTE[9:8]に格納した物理アドレス[51:50]を戻すためのシフト量42です。64KiB形式の36と共用すると違うアドレスになるので、ページサイズの条件で定義を切り替えます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PHYS_TO_PTE_ADDR_MASK GENMASK_ULL(49, 8)64KiB以外の52ビット物理アドレス形式で、アドレスを折り込んだ値のビット[49:8]を残します。__phys_to_pte_valがphysとphys>>42を合成したあとに使い、低位位置へ移した上位2ビットも保存します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#endif181행에서 시작한 선택 구간을 마칩니다. 64KiB 페이지의 52비트 물리 주소 형식은 상위 네 비트를 PTE의 15:12에 담습니다. 다른 페이지 크기의 LPA2 형식에서는 상위 두 비트를 9:8에 담으므로 마스크와 이동량을 별도로 정의합니다.
#endif180행에서 시작한 선택 구간을 마칩니다. 52비트 물리 주소를 지원하는 빌드에서는 PTE의 일반 주소 필드에 들어가지 않는 상위 주소 비트의 별도 인코딩을 정의합니다. 가상 주소 폭을 선택하는 CONFIG_ARM64_VA_BITS_52와 다른 설정입니다.
#define PTE_ATTRINDX(t) (_AT(pteval_t, (t)) << 2)メモリ属性の番号tをPTEのAttrIndx[2:0]に相当するビット[4:2]へ入れます。選んだMAIR属性からNormal・Deviceやキャッシュ動作が決まり、ここへ属性の全ビット列を直接入れるわけではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_ATTRINDX_MASK (_AT(pteval_t, 7) << 2)PTEのAttrIndxビット[4:2]を選ぶマスク0x1cです。ページの物理アドレスや権限を保ったまま、メモリ属性番号だけを抽出・置換するときに使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PI_IDX_0 6 /* AP[1], USER */Permission Indirectionで使う4ビット索引の最下位ビットが、PTEの位置6にあることを示します。値はビットマスクではなく位置番号で、pte_pi_indexがBIT(6)を使って索引のビット0へ移します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PI_IDX_1 51 /* DBM */Permission Indirection索引のビット1をPTEの位置51から取り出すための位置番号です。通常形式のDBMと同じ位置を索引として読み替えるため、値51そのものを権限マスクとしてORしません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PI_IDX_2 53 /* PXN */Permission Indirection索引のビット2がPTEの位置53にあることを表します。通常形式のPXN位置から1ビット取り出して、権限表を選ぶ4ビット値へまとめます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PI_IDX_3 54 /* UXN */Permission Indirection索引のビット3がPTEの位置54にあることを表します。通常形式のUXN位置を使い、他の3位置と合わせて権限を間接的に選ぶ索引を作ります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PO_IDX_0 (_AT(pteval_t, 1) << 60)Permission Overlayの索引ビット0をPTE位置60に置くマスクです。索引が選ぶ追加の権限制限をPOR_EL0などで適用するための情報であり、直接の読み書き許可ビットではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PO_IDX_1 (_AT(pteval_t, 1) << 61)Permission Overlay索引のビット1に対応するPTE位置61のマスクです。位置60・62と合わせて3ビットの保護キー索引を構成し、ページ表を作り替えず追加のアクセス制限を選ぶために使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PO_IDX_2 (_AT(pteval_t, 1) << 62)Permission Overlay索引のビット2に対応するPTE位置62のマスクです。3つの索引ビットで最大8個の値を表し、実際の許可・禁止内容は選んだoverlay設定側で決まります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define PTE_PO_IDX_MASK GENMASK_ULL(62, 60)Permission Overlay索引全体のPTE[62:60]を選ぶマスクです。FIELD_GETなどで3ビットのキーを取り出すときや、既存の索引だけを消して新しいキーに置き換えるときに使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
x86-64 · Linux 6.18.37
Present/RW/US/PWT/PCD/A/D/PS/NX와 software bit
4-level 또는 LA57 5-level이며 PMD/PUD의 PS bit가 large page leaf를 표시한다. PAT bit 위치는 4KiB와 huge PTE에서 달라지고 global bit는 CR3 switch 생존 범위에 영향을 준다.
원본 코드: arch/x86/include/asm/pgtable_types.h:43-109
43#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW2 /* Saved Dirty bit (leaf) */
44#define _PAGE_BIT_NOPTISHADOW _PAGE_BIT_SOFTW2 /* No PTI shadow (root PGD) */
45#endif
46
47/* If _PAGE_BIT_PRESENT is clear, we use these: */
48/* - if the user mapped it with PROT_NONE; pte_present gives true */
49#define _PAGE_BIT_PROTNONE _PAGE_BIT_GLOBAL
50
51#define _PAGE_PRESENT (_AT(pteval_t, 1) << _PAGE_BIT_PRESENT)
52#define _PAGE_RW (_AT(pteval_t, 1) << _PAGE_BIT_RW)
53#define _PAGE_USER (_AT(pteval_t, 1) << _PAGE_BIT_USER)
54#define _PAGE_PWT (_AT(pteval_t, 1) << _PAGE_BIT_PWT)
55#define _PAGE_PCD (_AT(pteval_t, 1) << _PAGE_BIT_PCD)
56#define _PAGE_ACCESSED (_AT(pteval_t, 1) << _PAGE_BIT_ACCESSED)
57#define _PAGE_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_DIRTY)
58#define _PAGE_PSE (_AT(pteval_t, 1) << _PAGE_BIT_PSE)
59#define _PAGE_GLOBAL (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL)
60#define _PAGE_SOFTW1 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW1)
61#define _PAGE_SOFTW2 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW2)
62#define _PAGE_SOFTW3 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW3)
63#define _PAGE_PAT (_AT(pteval_t, 1) << _PAGE_BIT_PAT)
64#define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)
65#define _PAGE_SPECIAL (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL)
66#define _PAGE_CPA_TEST (_AT(pteval_t, 1) << _PAGE_BIT_CPA_TEST)
67#define _PAGE_KERNEL_4K (_AT(pteval_t, 1) << _PAGE_BIT_KERNEL_4K)
68#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS
69#define _PAGE_PKEY_BIT0 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT0)
70#define _PAGE_PKEY_BIT1 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT1)
71#define _PAGE_PKEY_BIT2 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT2)
72#define _PAGE_PKEY_BIT3 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT3)
73#else
74#define _PAGE_PKEY_BIT0 (_AT(pteval_t, 0))
75#define _PAGE_PKEY_BIT1 (_AT(pteval_t, 0))
76#define _PAGE_PKEY_BIT2 (_AT(pteval_t, 0))
77#define _PAGE_PKEY_BIT3 (_AT(pteval_t, 0))
78#endif
79
80#define _PAGE_PKEY_MASK (_PAGE_PKEY_BIT0 | \
81 _PAGE_PKEY_BIT1 | \
82 _PAGE_PKEY_BIT2 | \
83 _PAGE_PKEY_BIT3)
84
85#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)
86#define _PAGE_KNL_ERRATUM_MASK (_PAGE_DIRTY | _PAGE_ACCESSED)
87#else
88#define _PAGE_KNL_ERRATUM_MASK 0
89#endif
90
91#ifdef CONFIG_MEM_SOFT_DIRTY
92#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_SOFT_DIRTY)
93#else
94#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0))
95#endif
96
97/*
98 * Tracking soft dirty bit when a page goes to a swap is tricky.
99 * We need a bit which can be stored in pte _and_ not conflict
100 * with swap entry format. On x86 bits 1-4 are *not* involved
101 * into swap entry computation, but bit 7 is used for thp migration,
102 * so we borrow bit 1 for soft dirty tracking.
103 *
104 * Please note that this bit must be treated as swap dirty page
105 * mark if and only if the PTE/PMD has present bit clear!
106 */
107#ifdef CONFIG_MEM_SOFT_DIRTY
108#define _PAGE_SWP_SOFT_DIRTY _PAGE_RW
109#else주요 구문 해설
원본 67줄은 그대로 표시하며, 검토한 49개 구문에 설명을 붙였습니다.
#define _PAGE_BIT_SAVED_DIRTY _PAGE_BIT_SOFTW2 /* Saved Dirty bit (leaf) */32ビットx86では保存用Dirty状態にソフトウェア用ビット10を割り当てます。書き込み禁止へ変える際も変更済み情報を別位置へ残せるようにする位置番号です。ここは64ビット側のビット58を使う定義と条件が異なります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_BIT_NOPTISHADOW _PAGE_BIT_SOFTW2 /* No PTI shadow (root PGD) */32ビットx86の最上位PGDで、PTI用ユーザー側ページ表へ複製しない印にビット10を使います。直前のSavedDirtyと同じ番号ですが、こちらはルートPGD、あちらは末端記述子という用途の違いで区別します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#endif38행에서 시작한 선택 구간을 마칩니다. 64비트와 32비트 PTE는 소프트웨어용 여유 비트의 배치가 다릅니다. saved-dirty와 PTI shadow 제외 표식은 64비트에서는 SOFTW5를, 32비트에서는 지원하지 않는 userfaultfd WP와 겹치는 SOFTW2 자리를 사용합니다.
#define _PAGE_BIT_PROTNONE _PAGE_BIT_GLOBALハードウェアPresentが0のとき、ビット8をLinuxのPROT_NONE印として再利用します。Presentが1なら同じ位置はGlobalですが、0ならCPUの有効変換にならないため、ソフトウェアがアクセス禁止の在住ページとswap項目を区別できます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PRESENT (_AT(pteval_t, 1) << _PAGE_BIT_PRESENT)PTE値のビット0、Presentを立てるマスクです。ハードウェアが有効な変換として使うかを示し、0の項目はLinuxがswapやPROT_NONEなど別の状態として解釈する場合があります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_RW (_AT(pteval_t, 1) << _PAGE_BIT_RW)ビット1の書き込み許可マスクです。読み取り専用から書き込み可能へ変更する際に使いますが、最終的なアクセス可否は上位ページ表やCPUの保護設定にも制約されます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_USER (_AT(pteval_t, 1) << _PAGE_BIT_USER)ビット2のUser/Supervisorマスクです。ユーザー側からアクセス可能なページとして指定するために使い、最上位から末端までの権限の組み合わせで最終的な可否が決まります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PWT (_AT(pteval_t, 1) << _PAGE_BIT_PWT)ビット3のPage Write Throughを表すマスクです。PATとPCDと組み合わせてメモリのキャッシュ種別を選ぶ入力に使います。ページの書き込み権限を許可するRWビットとは役割が異なります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PCD (_AT(pteval_t, 1) << _PAGE_BIT_PCD)ビット4のPage Cache Disableを表すマスクです。PAT・PWTと合わせてキャッシュ方式を選ぶために使い、単独のC変数フラグだけで任意のメモリを安全なMMIOへ変えられるわけではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_ACCESSED (_AT(pteval_t, 1) << _PAGE_BIT_ACCESSED)ビット5のAccessedマスクです。CPUが変換を使ったことを記録する状態で、カーネルはページが最近参照されたかの判断などに利用します。書き込みの有無は別のDirtyビットです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_DIRTY)ビット6のDirtyマスクです。末端マッピングを通じた書き込みで内容が変更されたことを記録し、カーネルが回収や書き戻しを判断する材料になります。ソフトウェアのsoft-dirty追跡とは別のビットです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PSE (_AT(pteval_t, 1) << _PAGE_BIT_PSE)ビット7のPage Size属性です。対応する上位記述子で立てると、次の表ではなく大きなページへのマッピングになります。同じ位置は4KiBの末端PTEではPATに使われるため記述子のレベルを区別します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_GLOBAL (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL)ビット8のGlobal属性です。有効な変換に付けると、対応するCR3切り替えで通常の非Global変換と同じようにはTLBから除かれない対象を表します。明示的な無効化まで禁止する属性ではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SOFTW1 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW1)ビット9のソフトウェア用領域を選ぶマスクです。CPUの読み書き権限そのものではなく、Linuxが記述子の用途に応じてSPECIALやCPA試験用の印として使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SOFTW2 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW2)ビット10のソフトウェア用マスクです。userfaultfdの書き込み保護追跡や、32ビット構成のSavedDirtyなど、設定と記述子の種類に応じたLinux内部の状態を保持します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SOFTW3 (_AT(pteval_t, 1) << _PAGE_BIT_SOFTW3)ビット11のソフトウェア用マスクです。ユーザー頁のsoft-dirty追跡と、カーネルの4KiB固定印などが文脈に応じてこの位置を使います。ハードウェアDirtyビット6とは異なります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PAT (_AT(pteval_t, 1) << _PAGE_BIT_PAT)4KiBの末端PTEでPAT索引に使うビット7のマスクです。PWT・PCDと合わせてメモリ種別を選びます。大きなページの記述子ではビット7がサイズ指定になるためPATの位置も変わります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)大きなページの記述子でPAT索引に使うビット12のマスクです。4KiB PTEのPAT位置7と分けることで、大ページ指定ビットとキャッシュ属性を取り違えずに変換できます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SPECIAL (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL)通常のstruct pageにそのまま対応づけない特殊なPTEをLinuxが識別するため、ソフトウェア用ビット9を使います。アクセス許可を追加するハードウェア権限ビットではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_CPA_TEST (_AT(pteval_t, 1) << _PAGE_BIT_CPA_TEST)ページ属性変更、CPAの試験で使うソフトウェア用ビット9のマスクです。試験対象へ印を付けて変更結果を調べるためのもので、CPUの実行・書き込み権限そのものを表しません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_KERNEL_4K (_AT(pteval_t, 1) << _PAGE_BIT_KERNEL_4K)カーネルのマッピングを4KiBページのまま保ち、大ページへまとめないためのソフトウェア印です。位置11を使い、下位表を結合できるかという判断でページサイズの意図を残します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#ifdef CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYSIntel protection keys는 같은 사용자 주소 공간 안에서 페이지 묶음별 읽기·쓰기를 PKRU로 제어하는 기능입니다. 켜지면 PTE의 네 비트 키 필드를 정의하고, 꺼지면 이 필드 마스크를 0으로 만들어 키 비트를 사용하지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#define _PAGE_PKEY_BIT0 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT0)Intelのメモリ保護キーをビルドする構成で、4ビット索引のビット0をPTE位置59へ置くマスクです。索引は最大16個のキーを選び、実際の読み書き制限は対応する保護キーレジスタの設定と組み合わせて決まります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PKEY_BIT1 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT1)Intelのメモリ保護キーをビルドする構成で、4ビット索引のビット1をPTE位置60へ置くマスクです。索引は最大16個のキーを選び、実際の読み書き制限は対応する保護キーレジスタの設定と組み合わせて決まります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PKEY_BIT2 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT2)Intelのメモリ保護キーをビルドする構成で、4ビット索引のビット2をPTE位置61へ置くマスクです。索引は最大16個のキーを選び、実際の読み書き制限は対応する保護キーレジスタの設定と組み合わせて決まります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PKEY_BIT3 (_AT(pteval_t, 1) << _PAGE_BIT_PKEY_BIT3)Intelのメモリ保護キーをビルドする構成で、4ビット索引のビット3をPTE位置62へ置くマスクです。索引は最大16個のキーを選び、実際の読み書き制限は対応する保護キーレジスタの設定と組み合わせて決まります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#elseprotection key 지원이 없으므로 PTE의 네 키 필드 마스크를 모두 0으로 둡니다. 공통 페이지 속성 코드가 키 비트를 설정하지 않도록 하는 정의입니다.
#define _PAGE_PKEY_BIT0 (_AT(pteval_t, 0))メモリ保護キーをビルドしない構成では索引ビット0のマスクを0にします。共通の属性合成式へ含めてもPTE位置59を立てず、未使用のキー情報を作らないためです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PKEY_BIT1 (_AT(pteval_t, 0))メモリ保護キーをビルドしない構成では索引ビット1のマスクを0にします。共通の属性合成式へ含めてもPTE位置60を立てず、未使用のキー情報を作らないためです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PKEY_BIT2 (_AT(pteval_t, 0))メモリ保護キーをビルドしない構成では索引ビット2のマスクを0にします。共通の属性合成式へ含めてもPTE位置61を立てず、未使用のキー情報を作らないためです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PKEY_BIT3 (_AT(pteval_t, 0))メモリ保護キーをビルドしない構成では索引ビット3のマスクを0にします。共通の属性合成式へ含めてもPTE位置62を立てず、未使用のキー情報を作らないためです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#endif68행에서 시작한 선택 구간을 마칩니다. Intel protection keys는 같은 사용자 주소 공간 안에서 페이지 묶음별 읽기·쓰기를 PKRU로 제어하는 기능입니다. 켜지면 PTE의 네 비트 키 필드를 정의하고, 꺼지면 이 필드 마스크를 0으로 만들어 키 비트를 사용하지 않습니다.
#define _PAGE_PKEY_MASK (_PAGE_PKEY_BIT0 | \保護キー索引を構成する4ビットをORしたマスクです。有効な構成ではPTE[62:59]をまとめて選び、キーの抽出や置換に使います。保護キー機能をビルドしない場合は各項が0なので全体も0です。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
_PAGE_PKEY_BIT1 | \PTE에 담는 4비트 보호 키 번호 중 bit 1의 마스크를 합칩니다. 이 값 자체가 접근 허용 여부는 아니며, 키 번호가 PKRU의 권한 항목을 선택합니다.
_PAGE_PKEY_BIT2 | \보호 키 번호의 bit 2까지 OR하여 페이지 표에서 키 필드만 골라낼 마스크를 만듭니다. 나머지 PTE 속성 비트는 포함하지 않습니다.
_PAGE_PKEY_BIT3)마지막 bit 3을 합쳐 4비트 보호 키 필드 전체의 마스크를 완성합니다. 보호 키 기능을 빌드하지 않으면 각 구성 비트가 0이므로 전체 마스크도 0입니다.
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)64비트 또는 PAE 페이지 표에서는 Knights Landing의 접근·dirty 비트 관련 우회를 위한 마스크를 제공합니다. 비PAE 32비트 형식은 해당 마스크를 0으로 둡니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#define _PAGE_KNL_ERRATUM_MASK (_PAGE_DIRTY | _PAGE_ACCESSED)x86-64またはPAEの構成で、空のページ表項目かを判定するときに除外するAccessed・Dirtyのマスクです。pte_noneなどがこれらを無視して判定することで、KNLのerratumに関連する残存ビットを有効マッピングと誤認しないようにします。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#else비PAE 32비트 페이지 표는 위의 Knights Landing 우회 비트 마스크를 사용하지 않아 0으로 정의합니다. 페이지의 일반 접근·dirty 비트 자체를 삭제하는 것은 아닙니다.
#define _PAGE_KNL_ERRATUM_MASK 0x86-64でもPAEでもない構成では、空の項目の判定から特別に除外するKNL対応ビットを0にします。共通の判定式でこのマスクを反転しても値が変わらず、余計な例外処理が入りません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#endif85행에서 시작한 선택 구간을 마칩니다. 64비트 또는 PAE 페이지 표에서는 Knights Landing의 접근·dirty 비트 관련 우회를 위한 마스크를 제공합니다. 비PAE 32비트 형식은 해당 마스크를 0으로 둡니다.
#ifdef CONFIG_MEM_SOFT_DIRTYsoft-dirty는 사용자 페이지가 기준 시점 이후 쓰였는지 소프트웨어가 추적하는 기능입니다. 켜지면 PTE에 추적 비트 마스크를 정의하고, 꺼지면 0으로 두어 해당 표식을 쓰지 않습니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_SOFT_DIRTY)CONFIG_MEM_SOFT_DIRTYを使うときのソフトウェア変更追跡ビット11です。ユーザー空間の変更追跡に利用し、CPUが立てるDirtyビット6とは別の履歴を表します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#elsesoft-dirty 추적을 빌드하지 않아 해당 PTE 표식 마스크를 0으로 만듭니다. 하드웨어의 실제 dirty 비트는 별도로 유지됩니다.
#define _PAGE_SOFT_DIRTY (_AT(pteval_t, 0))soft-dirty追跡をビルドしない場合は、この属性を型付き定数0にします。共通コードが保護属性へORしてもビットが追加されず、存在しない追跡状態を作らないためです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#endif91행에서 시작한 선택 구간을 마칩니다. soft-dirty는 사용자 페이지가 기준 시점 이후 쓰였는지 소프트웨어가 추적하는 기능입니다. 켜지면 PTE에 추적 비트 마스크를 정의하고, 꺼지면 0으로 두어 해당 표식을 쓰지 않습니다.
#ifdef CONFIG_MEM_SOFT_DIRTYsoft-dirty 추적을 켜면 swap entry에도 쓰기 흔적을 남길 수 있도록 비트 위치를 정합니다. 여기서는 present가 꺼진 entry의 _PAGE_RW 자리를 사용하며 실제 present PTE의 쓰기 허용 비트와 문맥을 구분해야 합니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#define _PAGE_SWP_SOFT_DIRTY _PAGE_RWPresentが0のswap項目では、通常のRW位置1をsoft-dirty情報の保存に使います。この状態の項目はハードウェアの有効変換ではないので、ビット1は書き込み許可ではなくswap中の変更履歴を意味します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#elsesoft-dirty 추적이 없으므로 swap entry용 soft-dirty 마스크도 0으로 둡니다. swap 기능 자체를 끄는 조건은 아닙니다.
RISC-V · Linux 6.18.37
V/R/W/X/U/G/A/D와 PPN field
R/W/X가 모두 0이면 next-level table이고 하나라도 설정되면 leaf다. Sv39, Sv48, Sv57 mode에 따라 root level이 달라지며 superpage leaf는 낮은 PPN field 정렬을 요구한다.
원본 코드: arch/riscv/include/asm/pgtable-bits.h:5-42
5
6#ifndef _ASM_RISCV_PGTABLE_BITS_H
7#define _ASM_RISCV_PGTABLE_BITS_H
8
9#define _PAGE_ACCESSED_OFFSET 6
10
11#define _PAGE_PRESENT (1 << 0)
12#define _PAGE_READ (1 << 1) /* Readable */
13#define _PAGE_WRITE (1 << 2) /* Writable */
14#define _PAGE_EXEC (1 << 3) /* Executable */
15#define _PAGE_USER (1 << 4) /* User */
16#define _PAGE_GLOBAL (1 << 5) /* Global */
17#define _PAGE_ACCESSED (1 << 6) /* Set by hardware on any access */
18#define _PAGE_DIRTY (1 << 7) /* Set by hardware on any write */
19#define _PAGE_SOFT (3 << 8) /* Reserved for software */
20
21#define _PAGE_SPECIAL (1 << 8) /* RSW: 0x1 */
22#define _PAGE_TABLE _PAGE_PRESENT
23
24/*
25 * _PAGE_PROT_NONE is set on not-present pages (and ignored by the hardware) to
26 * distinguish them from swapped out pages
27 */
28#define _PAGE_PROT_NONE _PAGE_GLOBAL
29
30/* Used for swap PTEs only. */
31#define _PAGE_SWP_EXCLUSIVE _PAGE_ACCESSED
32
33#define _PAGE_PFN_SHIFT 10
34
35/*
36 * when all of R/W/X are zero, the PTE is a pointer to the next level
37 * of the page table; otherwise, it is a leaf PTE.
38 */
39#define _PAGE_LEAF (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)
40
41#endif /* _ASM_RISCV_PGTABLE_BITS_H */
42 주요 구문 해설
원본 38줄은 그대로 표시하며, 검토한 19개 구문에 설명을 붙였습니다.
#ifndef _ASM_RISCV_PGTABLE_BITS_HRISC-V PTE 비트 정의 헤더가 중복 처리되지 않도록 하는 포함 보호 조건입니다. 페이지 변환 기능을 켜는 런타임 검사나 Kconfig 옵션은 아닙니다. 이 조건은 빌드 전처리 단계에서 포함할 코드를 선택하며 런타임 조건 분기가 아닙니다.
#define _ASM_RISCV_PGTABLE_BITS_Hこのヘッダーの二重取り込みを防ぐ印です。先頭の#ifndefと組み合わせ、一度定義を読んだ後のincludeでは本文を飛ばします。置換する値が空なのは数値を使わず定義済みかどうかだけを検査するためです。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_ACCESSED_OFFSET 6A bit가 clear이면 hardware 또는 software access-bit 처리 경로가 필요하다.
#define _PAGE_PRESENT (1 << 0)PTEのValidに相当するビット0の値1です。有効なページまたは次のページ表を指す記述子で使います。ページへの末端マッピングかは、このビットだけでなくR/W/Xの組み合わせも見て区別します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_READ (1 << 1) /* Readable */R bit는 load 허용뿐 아니라 W와의 합법적인 encoding 조건에도 관여한다.
#define _PAGE_WRITE (1 << 2) /* Writable */書き込み許可Wを表すビット2のマスク4です。通常の有効な書き込み可能ページではRも必要で、Wだけを立てたR=0の組み合わせを通常の読み書きページとして作ってはいけません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_EXEC (1 << 3) /* Executable */命令実行許可Xを表すビット3のマスク8です。データだけを置くページではこのビットを落とし、実行するコードのマッピングで必要な権限と組み合わせます。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_USER (1 << 4) /* User */ユーザーモードからアクセス可能なページを表すU、ビット4のマスクです。Supervisor側からのアクセスはSUMなど他の制御にも依存するため、この1ビットだけで全モードのアクセスが許可されるわけではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_GLOBAL (1 << 5) /* Global */G、ビット5のマスクで、変換が特定のASIDだけに属さないGlobalマッピングであることを示します。アドレス空間の切り替えとTLBの無効化範囲を決めるときに区別します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_ACCESSED (1 << 6) /* Set by hardware on any access */A bit가 clear이면 hardware 또는 software access-bit 처리 경로가 필요하다.
#define _PAGE_DIRTY (1 << 7) /* Set by hardware on any write */D、ビット7のDirty状態を表すマスクです。そのマッピングを通じた書き込みで内容が変更されたことを記録し、カーネルがページの回収や書き戻しを判断する際に使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SOFT (3 << 8) /* Reserved for software */ソフトウェア用のRSWビット[9:8]をまとめて選ぶマスク0x300です。ハードウェアのR/W/X権限とは別に、LinuxがPTEの種類などの補助状態を保存するために使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SPECIAL (1 << 8) /* RSW: 0x1 */RSWの下位、ビット8を特殊PTEの印に使います。通常のstruct pageに対応するページとして扱えないマッピングをLinuxが識別するためのソフトウェア情報です。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_TABLE _PAGE_PRESENT次のレベルのページ表を指す項目の基本値をValidビットだけにします。R/W/Xがすべて0なら末端ページではなく表へのポインタなので、ページのアクセス権と混同しないための名前です。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PROT_NONE _PAGE_GLOBALValidが0の項目ではGlobal位置5をPROT_NONEの印として再利用します。CPUには無効な項目のまま、Linuxはアクセス禁止にした在住ページとswap項目を区別できるようになります。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_SWP_EXCLUSIVE _PAGE_ACCESSEDswap用PTEではAccessed位置6を排他的なswap状態の印として使います。Validなページの参照履歴とは解釈が異なり、匿名ページの共有・コピー処理を判断するソフトウェア情報をswap項目に残します。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_PFN_SHIFT 10物理ページ番号PFNをPTEのビット10から格納するためのシフト量です。下位10ビットを属性などに空け、PFNを左へ10ビットずらしてアドレス部分を作ります。ページサイズそのものが1024バイトという意味ではありません。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#define _PAGE_LEAF (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC)R・W・Xのどれかが立っているかを調べるマスク0xeです。有効項目でこれらがすべて0なら次の表へのポインタ、それ以外なら末端のページ記述子という判別に使います。 이 이름과 인자는 전처리 단계에 정의된 내용으로 치환됩니다. 매크로 정의 자체가 CPU 명령 실행은 아닙니다.
#endif /* _ASM_RISCV_PGTABLE_BITS_H */6행에서 시작한 선택 구간을 마칩니다. RISC-V PTE 비트 정의 헤더가 중복 처리되지 않도록 하는 포함 보호 조건입니다. 페이지 변환 기능을 켜는 런타임 검사나 Kconfig 옵션은 아닙니다.
05 · WORKED EXAMPLE
숫자로 검산하기
VA 하나를 세 architecture의 page-table index로 분해
4KiB page와 48-bit VA에서 가상주소 0xffff800012345678을 예로 든다.
- offset하위 12bit는 0x678이다.
- PTE indexbit20:12는 (VA>>12)&0x1ff = 0x145다.
- PMD indexbit29:21은 0x091, PUD index bit38:30과 PGD index bit47:39도 같은 방식으로 구한다.
- architecture checkx86 canonical sign extension, arm64 TCR VA size, RISC-V 선택 SATP mode가 이 VA를 실제로 허용하는지 별도로 확인한다.
결론index 산술이 같아 보이는 4KiB/9-bit level 구성에서도 descriptor bit, top-level 수와 canonical address 규칙은 같지 않다.
06 · DEEP DIVE
경계별 상세 분석
공통 kernel core와 architecture hook의 경계
mm core는 pgd/p4d/pud/pmd/pte API를 사용하지만 folded level, leaf 허용 level, accessed/dirty hardware 지원과 execute permission encoding은 architecture가 정한다.
software PTE helper가 만든 raw value가 memory에 publish되고 TLB invalidate가 끝난 뒤에야 hardware permission이 바뀐다. PTE write와 shootdown을 하나의 transaction으로 읽는다.
arm64: valid/table bit와 AttrIndx, AP, SH, AF, UXN/PXN descriptor
4KiB granule 기준 최대 4/5 level을 사용하며 block descriptor가 PUD/PMD leaf를 만든다. UXN과 PXN이 user/kernel execute를 분리하고 AF/DBM이 access/dirty 관리에 관여한다.
break-before-make가 필요한 attribute/output-address 변경은 invalidation과 barrier 순서를 강제한다. 디버깅할 때는 raw PTE, level, output PA, AttrIndx, AP, SH, AF, UXN/PXN과 granule을 출력한다.
x86-64: Present/RW/US/PWT/PCD/A/D/PS/NX와 software bit
4-level 또는 LA57 5-level이며 PMD/PUD의 PS bit가 large page leaf를 표시한다. PAT bit 위치는 4KiB와 huge PTE에서 달라지고 global bit는 CR3 switch 생존 범위에 영향을 준다.
permission 감소는 TLB shootdown 전까지 remote CPU에서 효력이 없으며 A/D bit clear는 hardware update와 경쟁한다. 디버깅할 때는 CR4.LA57, EFER.NXE, raw PTE, PS/G/NX와 physical-address mask를 확인한다.
RISC-V: V/R/W/X/U/G/A/D와 PPN field
R/W/X가 모두 0이면 next-level table이고 하나라도 설정되면 leaf다. Sv39, Sv48, Sv57 mode에 따라 root level이 달라지며 superpage leaf는 낮은 PPN field 정렬을 요구한다.
invalid PTE에서 valid leaf로 publish한 뒤 local/remote sfence.vma가 old translation을 제거해야 한다. 디버깅할 때는 SATP.MODE, ASID, level index, V/R/W/X/U/G/A/D와 PPN 정렬을 확인한다.
객체 수명과 소유권을 먼저 고정한다
page-table page는 RCU/TLB grace period가 끝날 때까지 walker가 참조할 수 있다. leaf를 지웠다고 즉시 table page를 allocator에 돌려주면 remote walker가 재사용된 memory를 PTE로 읽는다.
주소나 register 값이 맞는지만 확인하면 stale state를 놓친다. producer, publication, consumer와 폐기 지점을 같은 표에 기록한다.
latency upper bound는 hardware instruction 하나가 아니다
page walk level 수, huge mapping hit, page-walk cache와 TLB miss가 합쳐진다. 4KiB mapping의 최악 memory reference 수와 2MiB/1GiB leaf의 감소를 따로 계산한다.
평균값 외에 interrupt-off 구간, remote CPU 응답, firmware 호출과 retry 횟수를 분리해야 최악 지연의 원인을 찾을 수 있다.
07 · FAILURE
실패를 어떤 증거로 나눌 것인가
| 분류 | 관찰되는 결과 | 첫 확인값 |
|---|---|---|
| arm64 | table descriptor를 block으로 잘못 해석하거나 BBM을 어기면 stale translation 또는 architecture fault가 난다. | raw PTE, level, output PA, AttrIndx, AP, SH, AF, UXN/PXN과 granule을 출력한다. |
| x86-64 | NXE, PAT, encryption C-bit 또는 software protnone encoding을 누락하면 permission과 cache type이 달라진다. | CR4.LA57, EFER.NXE, raw PTE, PS/G/NX와 physical-address mask를 확인한다. |
| RISC-V | W=1,R=0 같은 reserved encoding이나 misaligned superpage PPN은 page fault를 만든다. | SATP.MODE, ASID, level index, V/R/W/X/U/G/A/D와 PPN 정렬을 확인한다. |
08 · LAB
재현과 계측 절차
- 동일한 VA에 대해 page-table walker를 작성해 raw entry와 최종 PA를 세 architecture에서 출력한다.
- 4KiB mapping과 huge mapping의 TLB miss latency를 perf counter로 비교한다.
- 동일한 workload에서 세 architecture의 tracepoint 이름, CPU 번호, PC, stack pointer와 address-space identifier를 같은 열로 기록한다.
- 소스만 읽고 끝내지 않고 최종
vmlinux의objdump -dr,readelf -SW결과로 선택된 alternative와 section 배치를 확인한다.
09 · REFERENCES
원문 좌표
- arm64arch/arm64/include/asm/pgtable-hwdef.h:156-214
- x86-64arch/x86/include/asm/pgtable_types.h:43-109
- RISC-Varch/riscv/include/asm/pgtable-bits.h:5-42
Linux kernel source: GPL-2.0-only. 이 글의 코드 발췌는 Linux v6.18.37 원문을 기준으로 하며, 분석 문장은 해당 코드의 실행 조건과 상태 경계를 설명합니다.