Linuxカーネルの解説本は何種類か出版されているけど大概はx86_32が対象なんですよね。ということでx86_32を基本として大まかな内容は解説本を参考にしつつ、実際のx86_64の処理はコードを読む必要があるのです。。。 ということでx86_64の場合のページングの初期化部分のコードを読んでみる。
ページング設定に着た時点でx86_64のlong modeに入っているんだけど、ここまでの流れは前にlinuxのlong modeへの移行処理にという記事で書いているので興味のある人はそちらもどうぞm(__)m
見ているカーネルのバージョンは3.13です。
まず、ページングの初期化処理はどこから呼ばれるのかというとinit/main.cのstart_kernel()から。 このstart_kernel()でsetup_arch()を呼んでいるところが該当部分。
512 pr_notice("%s", linux_banner); 513 setup_arch(&command_line);
x86_64の場合、setup_arch()はarch/x86/kernel/setup.cにある。この関数はx86_64、x86_32共通でx86_32固有部分は#ifdef CONFIG_X86_32やCONFIG_X86_64で切り分けている。
839 void __init setup_arch(char **cmdline_p) 840 { 841 memblock_reserve(__pa_symbol(_text), 842 (unsigned long)__bss_stop - (unsigned long)_text); 843 844 early_reserve_initrd(); 845 846 /* 847 * At this point everything still needed from the boot loader 848 * or BIOS or kernel text should be early reserved or marked not 849 * RAM in e820. All other memory is free game. 850 */ 851 852 #ifdef CONFIG_X86_32 853 memcpy(&boot_cpu_data, &new_cpu_data, sizeof(new_cpu_data)); 854 visws_early_detect(); 855 856 /* 857 * copy kernel address range established so far and switch 858 * to the proper swapper page table 859 */ 860 clone_pgd_range(swapper_pg_dir + KERNEL_PGD_BOUNDARY, 861 initial_page_table + KERNEL_PGD_BOUNDARY, 862 KERNEL_PGD_PTRS); 863 864 load_cr3(swapper_pg_dir); 865 __flush_tlb_all(); 866 #else 867 printk(KERN_INFO "Command line: %s\n", boot_command_line); 868 #endif 869 〜略〜
ページングとは関係ないけど、memblock_reserve()で第1引数のアドレスから第2引数のアドレスまでの範囲のメモリ領域を確保したり、x86の場合はページグローバルディレクトリの切り替えが行われたりしている。
それで、実際にページングの初期化処理を呼んでいるのはだいぶ飛んで1149行目。
1145 #ifdef CONFIG_KVM_GUEST 1146 kvmclock_init(); 1147 #endif 1148 1149 x86_init.paging.pagetable_init();
これはx86_initはx86の初期化に使う関数を設定している構造体。ファイルはarch/x86/kernel/x86_init.cでページングの初期化にはnative_pagetable_init()とい関数が設定されている。
33 /* 34 * The platform setup functions are preset with the default functions 35 * for standard PC hardware. 36 */ 37 struct x86_init_ops x86_init __initdata = { 38 39 .resources = { 40 .probe_roms = probe_roms, 41 .reserve_resources = reserve_standard_io_resources, 42 .memory_setup = default_machine_specific_memory_setup, 43 }, 44 45 .mpparse = { 46 .mpc_record = x86_init_uint_noop, 47 .setup_ioapic_ids = x86_init_noop, 48 .mpc_apic_id = default_mpc_apic_id, 49 .smp_read_mpc_oem = default_smp_read_mpc_oem, 50 .mpc_oem_bus_info = default_mpc_oem_bus_info, 51 .find_smp_config = default_find_smp_config, 52 .get_smp_config = default_get_smp_config, 53 }, 54 55 .irqs = { 56 .pre_vector_init = init_ISA_irqs, 57 .intr_init = native_init_IRQ, 58 .trap_init = x86_init_noop, 59 }, 60 61 .oem = { 62 .arch_setup = x86_init_noop, 63 .banner = default_banner, 64 }, 65 66 .paging = { 67 .pagetable_init = native_pagetable_init, 68 }, 69 70 .timers = { 71 .setup_percpu_clockev = setup_boot_APIC_clock, 72 .tsc_pre_init = x86_init_noop, 73 .timer_init = hpet_time_init, 74 .wallclock_init = x86_init_noop, 75 }, 76 77 .iommu = { 78 .iommu_init = iommu_init_noop, 79 }, 80 81 .pci = { 82 .init = x86_default_pci_init, 83 .init_irq = x86_default_pci_init_irq, 84 .fixup_irqs = x86_default_pci_fixup_irqs, 85 }, 86 };
native_pagetable_init()はどんなものかとういと、arch/x86/include/asm/pgtable_types.hで設定があって、x86かx86_64かで使うものが変わる。
354 #ifdef CONFIG_X86_32 355 extern void native_pagetable_init(void); 356 #else 357 #define native_pagetable_init paging_init 358 #endif
x86_64の場合はpaging_init()になり、arch/x86/mm/init_64.cに実体がある。
650 void __init paging_init(void) 651 { 652 sparse_memory_present_with_active_regions(MAX_NUMNODES); 653 sparse_init(); 654 655 /* 656 * clear the default setting with node 0 657 * note: don't use nodes_clear here, that is really clearing when 658 * numa support is not compiled in, and later node_set_state 659 * will not set it back. 660 */ 661 node_clear_state(0, N_MEMORY); 662 if (N_MEMORY != N_NORMAL_MEMORY) 663 node_clear_state(0, N_NORMAL_MEMORY); 664 665 zone_sizes_init(); 666 } 667
これは短い関数なんだけど、使用している関数の内容はちゃんと調べないといけないので次は(sparse_memory_present_with_active_regions())http://lxr.free-electrons.com/source/mm/page_alloc.c#L4380から調べよう。
ちなみにx86_32の場合のnative_pagetable_init()はこのような関数でx86_64よりも内容がわかりやすい。
452 void __init native_pagetable_init(void) 453 { 454 unsigned long pfn, va; 455 pgd_t *pgd, *base = swapper_pg_dir; 456 pud_t *pud; 457 pmd_t *pmd; 458 pte_t *pte; 459 460 /* 461 * Remove any mappings which extend past the end of physical 462 * memory from the boot time page table. 463 * In virtual address space, we should have at least two pages 464 * from VMALLOC_END to pkmap or fixmap according to VMALLOC_END 465 * definition. And max_low_pfn is set to VMALLOC_END physical 466 * address. If initial memory mapping is doing right job, we 467 * should have pte used near max_low_pfn or one pmd is not present. 468 */ 469 for (pfn = max_low_pfn; pfn < 1<<(32-PAGE_SHIFT); pfn++) { 470 va = PAGE_OFFSET + (pfn<<PAGE_SHIFT); 471 pgd = base + pgd_index(va); 472 if (!pgd_present(*pgd)) 473 break; 474 475 pud = pud_offset(pgd, va); 476 pmd = pmd_offset(pud, va); 477 if (!pmd_present(*pmd)) 478 break; 479 480 /* should not be large page here */ 481 if (pmd_large(*pmd)) { 482 pr_warn("try to clear pte for ram above max_low_pfn: pfn: %lx pmd: %p pmd phys: %lx, but pmd is big page and is not using pte !\n", 483 pfn, pmd, __pa(pmd)); 484 BUG_ON(1); 485 } 486 487 pte = pte_offset_kernel(pmd, va); 488 if (!pte_present(*pte)) 489 break; 490 491 printk(KERN_DEBUG "clearing pte for ram above max_low_pfn: pfn: %lx pmd: %p pmd phys: %lx pte: %p pte phys: %lx\n", 492 pfn, pmd, __pa(pmd), pte, __pa(pte)); 493 pte_clear(NULL, va, pte); 494 } 495 paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT); 496 paging_init(); 497 }
と思ったら大間違いで、最後に呼び出しているpaging_init()を見るとsparse_memory_present_with_active_regions()、sparse_init()、zone_sizes_init()はここでも使われていることがわかり、やっぱりこれらを調べないとダメというのがわかる\(^o^)/
695 * that we can trap those pesky NULL-reference errors in the kernel. 696 */ 697 void __init paging_init(void) 698 { 699 pagetable_init(); 700 701 __flush_tlb_all(); 702 703 kmap_init(); 704 705 /* 706 * NOTE: at this point the bootmem allocator is fully available. 707 */ 708 olpc_dt_build_devicetree(); 709 sparse_memory_present_with_active_regions(MAX_NUMNODES); 710 sparse_init(); 711 zone_sizes_init(); 712 }
(´-`).。oO(ただ、x86_32でも使っているということはどこかで解説されている可能性もあるかも
- 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/02/26
- メディア: 大型本
- 購入: 9人 クリック: 269回
- この商品を含むブログ (68件) を見る