retrage.github.io

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.Sstartup_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」に修正.

参考文献