Linux kernelの5-Level Paging有効化部分を読んでみる
この記事はLinux Advent Calendar 14日目の記事として書かれた. 本記事ではLinuxにおける5-Level Paging(la57 paging)の実装を見ていく.
5-Level Pagingとは
これまで,x86_64では物理アドレス下位48bitのみが使用されてきた. このため,64TiBの物理アドレス空間と256TiBの仮想アドレス空間に限られてきた. Intelはこれを拡張し,物理アドレス下位56bitへと拡張させた. これにより,4PiBの物理アドレス空間と128PiBの仮想アドレス空間が利用できるようになった. この拡張に対応するため新たに導入されたのが,ここで紹介する5-Level Pagingである. 基本的な考え方は4-Level Pagingと同一であり, PML4にさらに上位にPML5が追加された形となっている.
サポート状況について
現在,一部のサーバが5-Level Pagingに対応しているようである. では,個人では触ることができないか,というとそんなことはなく, QEMUのmasterブランチには5-Level Pagingのエミュレーションが 取り込まれている. このため,対応ハードウェアを持っていないくても楽しむことができる. 面白いことに,Library OSの1つであるOSvにおいて, Avi Kivityさんが5-Level Paging対応のパッチを 投稿している.
5-Level Pagingの確認
Linux kernerでコンパイル時に5-Level Pagingを有効にするかを決めているので, あまり関係がないが一応記しておく. 5-Level Pagingの機能を持っているかどうかはCPUID命令を用いることで確かめることができる. 具体的には
mov $7, %eax
mov $0, %ecx
cpuid
test $1<<16, %ecx /* ? */
jz .no_5level_paging
/* 以下,5Level Paging有効化のコード */
となる.
5-Level Pagingの有効化
次に実際に5-Level Pagingを以下のような流れで有効にする.
- 現在のPagingとmodeを確認
- long modeの場合はcompatibility modeなどに切り替える
- Pagingを無効化
- PAEとLA57の有効化
- WRMSRでIA32_EFER.LMEを設定
- CR3が5-Level Pagingを指すように設定
- Pagingの有効化
ここで注意したい点は,long modeでは直接LA57を変更できない点である. long modeでLA57を変更しようとした場合,general protection exceptionが発生するようである.
Linux kernelでの実装
では実際のコードを見ていく.
なお,参照するkernelはtorvalds/linux v4.14 bebc608 である.
arch/x86/boot/compressed/head_64.S
のstartup_64
のコードを見ていく.
おおよそコード内のコメントにある通りであるが,簡単コメントしていく.
ここでは,long modeが有効になっており,まだカーネルが圧縮されている状態である.
movq %cr4, %rax
testl $X86_CR4_LA57, %eax
jnz lvl5
最初にcr4レジスタの値から5-Level Pagingが有効であるかを確認する. 有効でなければ以下を行う.
leaq lvl5_pgtable(%rbx), %rdi
xorq %rax, %rax
movq $(PAGE_SIZE/8), %rcx
rep stosq
追加のPML5のテーブル(lvl5_pgtable)をクリアする.
movq %cr3, %rdi
leaq 0x7 (%rdi), %rax
movq %rax, lvl5_pgtable(%rbx)
ここでCR3を最初かつ勇逸のトップレベルページテーブルのエントリとするらしい.
pushq $__KERNEL32_CS
leaq compatible_mode(%rip), %rax
pushq %rax
lretq
compatibility modeに移行
compatible_modeの内容は次にようになっている.
movl %cr0, %eax
btrl $X86_CR0_PG_BIT, %eax
movl %eax, %cr0
Pagingを無効
leal lvl5_pgtable(%ebx), %eax
movl %eax, %cr3
CR3がPML5のテーブルを指すように設定
movl %cr4, %eax
orl $(X86_CR4_PAE | X86_CR4_LA57), %eax
movl %eax, %cr4
PAEとLA57の有効化
movl $(X86_CR0_PG | X86_CR0_PE), %eax
movl %eax, %cr0
Pagingの有効化
以上のような流れとなっている. 意外と短くまとまっていることがわかる.
2019-03-13 追記
指摘があったので,「物理アドレス下位52bit」を「物理アドレス下位56bit」に修正.
参考文献
- https://software.intel.com/sites/default/files/managed/2b/80/5-level_paging_white_paper.pdf
- https://lwn.net/Articles/708526/
- https://lwn.net/Articles/717293/
- https://lwn.net/Articles/716324/
- https://github.com/qemu/qemu/commit/6c7c3c21f95dd9af8a0691c0dd29b07247984122#diff-0517790436e7f5831f2b14f1e93ac740
- https://groups.google.com/forum/#!topic/osv-dev/9Y1q0j5qhXI