2012年5月5日土曜日

初期ページテーブル

x86_64 Linux 起動時のページテーブルをみてみる。
ページグローバルディレクトリ等の用語はx86_64ではなくLinuxのものに従います。
以下のソースコードは linux-3.4-rc5/arch/x86/kernel/head_64.S です。

ページグローバルディレクトリ

まずページグローバルディレクトリ。
 /*
  * This default setting generates an ident mapping at address 0x100000
  * and a mapping for the kernel that precisely maps virtual address
  * 0xffffffff80000000 to physical address 0x000000. (always using
  * 2Mbyte large pages provided by PAE mode)
  */
NEXT_PAGE(init_level4_pgt)
 .quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
 .org init_level4_pgt + L4_PAGE_OFFSET*8, 0
 .quad level3_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
 .org init_level4_pgt + L4_START_KERNEL*8, 0
 /* (2^48-(2*1024*1024*1024))/(2^39) = 511 */
 .quad level3_kernel_pgt - __START_KERNEL_map + _PAGE_TABLE
コメント中にでてくるident mappingというのはリニアアドレスと物理アドレスが等しくなるマッピングのことのよう。 ここでは3つのエントリを定義している。
entry index address
          0 0x0000000000000000-0x0000007fffffffff
        272 0xffff880000000000-0xffff887fffffffff
        511 0xffffff8000000000-0xffffffffffffffff
エントリは512個ありますが、表にないものは無効なエントリです。
x86_64においてはアドレスの63-48ビットは47ビットの符号拡張になるのでエントリ番号272と511の上位2バイトが0xffffになっていることに注意。

ページアッパーディレクトリ

二つのページアッパーディレクトリが定義されている。
NEXT_PAGE(level3_ident_pgt)
 .quad level2_ident_pgt - __START_KERNEL_map + _KERNPG_TABLE
 .fill 511,8,0

NEXT_PAGE(level3_kernel_pgt)
 .fill L3_START_KERNEL,8,0
 /* (2^48-(2*1024*1024*1024)-((2^39)*511))/(2^30) = 510 */
 .quad level2_kernel_pgt - __START_KERNEL_map + _KERNPG_TABLE
 .quad level2_fixmap_pgt - __START_KERNEL_map + _PAGE_TABLE
一つ目のlevel3_ident_pgtには先頭の1GBをマップするもの。
entry index address
          0 0x0000000000000000-0x000000003fffffff
二つ目のlevel3_kernel_pgtはカーネル用にお尻の2Gをマップするためのもの。
entry index address
        510 0x0000007f80000000-0x0000007fbfffffff
        511 0x0000007fc0000000-0x0000007fffffffff

ページミドルディレクトリ

これは四つ定義されている。まずそのうちの二つについて。
NEXT_PAGE(level2_ident_pgt)
 /* Since I easily can, map the first 1G.
  * Don't set NX because code runs from these pages.
  */
 PMDS(0, __PAGE_KERNEL_IDENT_LARGE_EXEC, PTRS_PER_PMD)

NEXT_PAGE(level2_kernel_pgt)
 /*
  * 512 MB kernel mapping. We spend a full page on this pagetable
  * anyway.
  *
  * The kernel code+data+bss must not be bigger than that.
  *
  * (NOTE: at +512MB starts the module area, see MODULES_VADDR.
  *  If you want to increase this then increase MODULES_VADDR
  *  too.)
  */
 PMDS(0, __PAGE_KERNEL_LARGE_EXEC,
  KERNEL_IMAGE_SIZE/PMD_SIZE)
一つ目のlevel2_ident_pgtはマクロでテーブルの全エントリをラージページ属性の有効なエントリとして定義さている。ラージページなので対応するページテーブルは必要ない。
次のlevel2_kernel_pgtはKERNEL_IMAGESIZE(512MBとdefineされている)分だけ有効なエントリを定義する。これもらラージページ属性付きなので対応するペーテーブルはない。
三つめはラージページ属性なしでページテーブルを使用するエントリとして定義する。
NEXT_PAGE(level2_fixmap_pgt)
 .fill 506,8,0
 .quad level1_fixmap_pgt - __START_KERNEL_map + _PAGE_TABLE
 /* 8MB reserved for vsyscalls + a 2MB hole = 4 + 1 entries */
 .fill 5,8,0
entry index address
        506 0x000000003f400000-0x000000003f5fffff

ページテーブル

最後のページテーブルは場所だけ確保されていて内容は定義されていない。
NEXT_PAGE(level1_fixmap_pgt)
 .fill 512,8,0

まとめ

以上を合わせると起動時には次のアドレスを変換するためのページテーブルが用意されていることになる。
0x0000000000000000-0x000000003fffffff
0xffff880000000000-0xffffff803fffffff
0xffffffff80000000-0xffffffff9fffffff
0xffffffffff400000-0xffffffffff5fffff

0 件のコメント:

コメントを投稿