Linux-3.14.12カーネルのメモリ管理ノート[ページテーブルを構築(3)

すでにローエンドメモリ・ページ・テーブルに)(init_mem_mappingに戻った後、カーネルのページテーブルとカーネルのメモリ不足のページテーブルの設立のための準備を分析した直後にも設立し、次の()関数のearly_ioremap_page_table_range_initがあります。

【file:/arch/x86/mm/init.c】
/*
 * Build a proper pagetable for the kernel mappings. Up until this
 * point, we've been running on some set of pagetables constructed by
 * the boot process.
 *
 * If we're booting on native hardware, this will be a pagetable
 * constructed in arch/x86/kernel/head_32.S. The root of the
 * pagetable will be swapper_pg_dir.
 *
 * If we're booting paravirtualized under a hypervisor, then there are
 * more options: we may already be running PAE, and the pagetable may
 * or may not be based in swapper_pg_dir. In any case,
 * paravirt_pagetable_init() will set up swapper_pg_dir
 * appropriately for the rest of the initialization to work.
 *
 * In general, pagetable_init() assumes that the pagetable may already
 * be partially populated, and so it avoids stomping on any existing
 * mappings.
 */
void __init early_ioremap_page_table_range_init(void)
{
    pgd_t *pgd_base = swapper_pg_dir;
    unsigned long vaddr, end;
 
    /*
     * Fixed mappings, only the page table structure has to be
     * created - mappings will be set by set_fixmap():
     */
    vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
    end = (FIXADDR_TOP + PMD_SIZE - 1) & PMD_MASK;
    page_table_range_init(vaddr, end, pgd_base);
    early_ioremap_reset();
}

この機能は、主に固定メモリマップ領域を確立するために使用されます。固定メモリマップ領域はFIXADDR_START FIXADDR_TOPアドレス空間を参照し、そのアドレス空間は、インデックスが列挙fixed_addresses列挙型形態の中で定義されるインデックスによって区別異なる特徴によって定義されます。

【file:/arch/x86/include/asm/fixmap.h】
/*
 * Here we define all the compile-time 'special' virtual
 * addresses. The point is to have a constant address at
 * compile time, but to set the physical address only
 * in the boot process.
 * for x86_32: We allocate these special addresses
 * from the end of virtual memory (0xfffff000) backwards.
 * Also this lets us do fail-safe vmalloc(), we
 * can guarantee that these special addresses and
 * vmalloc()-ed addresses never overlap.
 *
 * These 'compile-time allocated' memory buffers are
 * fixed-size 4k pages (or larger if used with an increment
 * higher than 1). Use set_fixmap(idx,phys) to associate
 * physical memory with fixmap indices.
 *
 * TLB entries of such buffers will not be flushed across
 * task switches.
 */
enum fixed_addresses {
#ifdef CONFIG_X86_32
    FIX_HOLE,
    FIX_VDSO,
#else
    VSYSCALL_LAST_PAGE,
    VSYSCALL_FIRST_PAGE = VSYSCALL_LAST_PAGE
                + ((VSYSCALL_END-VSYSCALL_START) >> PAGE_SHIFT) - 1,
    VVAR_PAGE,
    VSYSCALL_HPET,
#ifdef CONFIG_PARAVIRT_CLOCK
    PVCLOCK_FIXMAP_BEGIN,
    PVCLOCK_FIXMAP_END = PVCLOCK_FIXMAP_BEGIN+PVCLOCK_VSYSCALL_NR_PAGES-1,
#endif
#endif
    FIX_DBGP_BASE,
    FIX_EARLYCON_MEM_BASE,
#ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
    FIX_OHCI1394_BASE,
#endif
#ifdef CONFIG_X86_LOCAL_APIC
    FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
#endif
#ifdef CONFIG_X86_IO_APIC
    FIX_IO_APIC_BASE_0,
    FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS - 1,
#endif
#ifdef CONFIG_X86_VISWS_APIC
    FIX_CO_CPU, /* Cobalt timer */
    FIX_CO_APIC, /* Cobalt APIC Redirection Table */
    FIX_LI_PCIA, /* Lithium PCI Bridge A */
    FIX_LI_PCIB, /* Lithium PCI Bridge B */
#endif
    FIX_RO_IDT, /* Virtual mapping for read-only IDT */
#ifdef CONFIG_X86_32
    FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
    FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#ifdef CONFIG_PCI_MMCONFIG
    FIX_PCIE_MCFG,
#endif
#endif
#ifdef CONFIG_PARAVIRT
    FIX_PARAVIRT_BOOTMAP,
#endif
    FIX_TEXT_POKE1, /* reserve 2 pages for text_poke() */
    FIX_TEXT_POKE0, /* first page is last, because allocation is backward */
#ifdef CONFIG_X86_INTEL_MID
    FIX_LNW_VRTC,
#endif
    __end_of_permanent_fixed_addresses,
 
    /*
     * 256 temporary boot-time mappings, used by early_ioremap(),
     * before ioremap() is functional.
     *
     * If necessary we round it up to the next 256 pages boundary so
     * that we can have a single pgd entry and a single pte table:
     */
#define NR_FIX_BTMAPS 64
#define FIX_BTMAPS_SLOTS 4
#define TOTAL_FIX_BTMAPS (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)
    FIX_BTMAP_END =
     (__end_of_permanent_fixed_addresses ^
      (__end_of_permanent_fixed_addresses + TOTAL_FIX_BTMAPS - 1)) &
     -PTRS_PER_PTE
     ? __end_of_permanent_fixed_addresses + TOTAL_FIX_BTMAPS -
       (__end_of_permanent_fixed_addresses & (TOTAL_FIX_BTMAPS - 1))
     : __end_of_permanent_fixed_addresses,
    FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,
#ifdef CONFIG_X86_32
    FIX_WP_TEST,
#endif
#ifdef CONFIG_INTEL_TXT
    FIX_TBOOT_BASE,
#endif
    __end_of_fixed_addresses
};

しかし、各パーティションの列挙は、低アドレスから高分布に対処するために特定されますが、高いアドレスから低いアドレス配布にされていません。__fix_to_virtマクロは、固定インデックスによってマッピングされた領域に対応するリニアアドレスを計算するために使用されます。

#define __fix_to_virt(x)         (FIXADDR_TOP - ((x) << PAGE_SHIFT))

インデックス・マクロを、対応する仮想アドレス変換:

#define __virt_to_fix(x)         ((FIXADDR_TOP - ((x)&PAGE_MASK)) >> PAGE_SHIFT)

そして、バックearly_ioremap_page_table_range_initに())(第一page_table_range_init機能:

【file:/arch/x86/mm/init_32.c】
/*
 * This function initializes a certain range of kernel virtual memory
 * with new bootmem page tables, everywhere page tables are missing in
 * the given range.
 *
 * NOTE: The pagetables are allocated contiguous on the physical space
 * so we can cache the place of the first one and move around without
 * checking the pgd every time.
 */
static void __init
page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd_base)
{
    int pgd_idx, pmd_idx;
    unsigned long vaddr;
    pgd_t *pgd;
    pmd_t *pmd;
    pte_t *pte = NULL;
    unsigned long count = page_table_range_init_count(start, end);
    void *adr = NULL;
 
    if (count)
        adr = alloc_low_pages(count);
 
    vaddr = start;
    pgd_idx = pgd_index(vaddr);
    pmd_idx = pmd_index(vaddr);
    pgd = pgd_base + pgd_idx;
 
    for ( ; (pgd_idx < PTRS_PER_PGD) && (vaddr != end); pgd++, pgd_idx++) {
        pmd = one_md_table_init(pgd);
        pmd = pmd + pmd_index(vaddr);
        for (; (pmd_idx < PTRS_PER_PMD) && (vaddr != end);
                            pmd++, pmd_idx++) {
            pte = page_table_kmap_check(one_page_table_init(pmd),
                            pmd, vaddr, pte, &adr);
 
            vaddr += PMD_SIZE;
        }
        pmd_idx = 0;
    }
}
 

page_table_range_init_count()は、一時的なカーネルのページテーブルマッピング部の数を計算するための手段。FIXADDR_STARTカーネルマッピング領域をFIX_KMAP_ENDするための一時的な間隔FIX_KMAP_BEGINそのうちの1つは、インデックスラベル部異なる複数の機能との間のマッピング、マッピング領域をFIXADDR_TOPに固定挙げます。あなたは、両方の定義を見ることができます方法:

FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */

FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,

ページフレームのいずれかの上位メモリに「窓」の数を意味KM_TYPE_NRはkmap_atomicは、ハイエンドメモリとの間の関係、一時的なカーネルマッピングのつまり設立に「ウィンドウ」を構築することができます呼び出し、カーネルアドレス空間に「窓」を通じてマッピングすることができます。NR_CPUSは、CPUの数を示します。全体的にこれは、指定されたウィンドウのスペースを準備するために、各CPUのための一時的なカーネルマッピング範囲です。間隔は、ページ・テーブルの連続性を保証しなければならないように、kmap_atomic間隔を使用して)(以来。

ページ番号がpage_table_range_init_count(続く時間のグローバルディレクトリ、ゼロでない場合)alloc_low_pages()です:

【file:/arch/x86/mm/init.c】
/*
 * Pages returned are already directly mapped.
 *
 * Changing that is likely to break Xen, see commit:
 *
 * 279b706 x86,xen: introduce x86_init.mapping.pagetable_reserve
 *
 * for detailed information.
 */
__ref void *alloc_low_pages(unsigned int num)
{
    unsigned long pfn;
    int i;
 
    if (after_bootmem) {
        unsigned int order;
 
        order = get_order((unsigned long)num << PAGE_SHIFT);
        return (void *)__get_free_pages(GFP_ATOMIC | __GFP_NOTRACK |
                        __GFP_ZERO, order);
    }
 
    if ((pgt_buf_end + num) > pgt_buf_top || !can_use_brk_pgt) {
        unsigned long ret;
        if (min_pfn_mapped >= max_pfn_mapped)
            panic("alloc_low_pages: ran out of memory");
        ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT,
                    max_pfn_mapped << PAGE_SHIFT,
                    PAGE_SIZE * num , PAGE_SIZE);
        if (!ret)
            panic("alloc_low_pages: can not alloc memory");
        memblock_reserve(ret, PAGE_SIZE * num);
        pfn = ret >> PAGE_SHIFT;
    } else {
        pfn = pgt_buf_end;
        pgt_buf_end += num;
        printk(KERN_DEBUG "BRK [%#010lx, %#010lx] PGTABLE\n",
            pfn << PAGE_SHIFT, (pgt_buf_end << PAGE_SHIFT) - 1);
    }
 
    for (i = 0; i < num; i++) {
        void *adr;
 
        adr = __va((pfn + i) << PAGE_SHIFT);
        clear_page(adr);
    }
 
    return __va(pfn << PAGE_SHIFT);
}

上記はearly_alloc_pgt_buf()が、バッファ領域の使用を決定するために、ページテーブルを保持するために適用され、アルゴリズムは、ページ・テーブル・バッファからmemblockページテーブルメモリ空間によってアプリケーションまたはアプリケーションです。

()ページディレクトリの途中など、新しい物理ページに適用され、パラメータが空PGDがあるときに使用されているが、非PAEのx86環境の状況の分析のみが、ページディレクトリの真ん中が存在しないone_md_table_init戻るpage_table_range_initは()、それはあります実際に上院に遺骨を返します。コード付き:

【file:/arch/x86/mm/init_32.c】
/*
 * Creates a middle page table and puts a pointer to it in the
 * given global directory entry. This only returns the gd entry
 * in non-PAE compilation mode, since the middle layer is folded.
 */
static pmd_t * __init one_md_table_init(pgd_t *pgd)
{
    pud_t *pud;
    pmd_t *pmd_table;
 
#ifdef CONFIG_X86_PAE
    if (!(pgd_val(*pgd) & _PAGE_PRESENT)) {
        pmd_table = (pmd_t *)alloc_low_page();
        paravirt_alloc_pmd(&init_mm, __pa(pmd_table) >> PAGE_SHIFT);
        set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT));
        pud = pud_offset(pgd, 0);
        BUG_ON(pmd_table != pmd_offset(pud, 0));
 
        return pmd_table;
    }
#endif
    pud = pud_offset(pgd, 0);
    pmd_table = pmd_offset(pud, 0);
 
    return pmd_table;
}

基準点がページ・テーブルを作成し、ページ・テーブルが作成される指すように、ページ・テーブルをpmdをしない場合、基準one_page_table_init()が呼び出されpage_table_kmap_check()は、使用されています。page_table_kmap_check()の実装:

【file:/arch/x86/mm/init_32.c】
static pte_t *__init page_table_kmap_check(pte_t *pte, pmd_t *pmd,
                       unsigned long vaddr, pte_t *lastpte,
                       void **adr)
{
#ifdef CONFIG_HIGHMEM
    /*
     * Something (early fixmap) may already have put a pte
     * page here, which causes the page table allocation
     * to become nonlinear. Attempt to fix it, and if it
     * is still nonlinear then we have to bug.
     */
    int pmd_idx_kmap_begin = fix_to_virt(FIX_KMAP_END) >> PMD_SHIFT;
    int pmd_idx_kmap_end = fix_to_virt(FIX_KMAP_BEGIN) >> PMD_SHIFT;
 
    if (pmd_idx_kmap_begin != pmd_idx_kmap_end
        && (vaddr >> PMD_SHIFT) >= pmd_idx_kmap_begin
        && (vaddr >> PMD_SHIFT) <= pmd_idx_kmap_end) {
        pte_t *newpte;
        int i;
 
        BUG_ON(after_bootmem);
        newpte = *adr;
        for (i = 0; i < PTRS_PER_PTE; i++)
            set_pte(newpte + i, pte[i]);
        *adr = (void *)(((unsigned long)(*adr)) + PAGE_SIZE);
 
        paravirt_alloc_pte(&init_mm, __pa(newpte) >> PAGE_SHIFT);
        set_pmd(pmd, __pmd(__pa(newpte)|_PAGE_TABLE));
        BUG_ON(newpte != pte_offset_kernel(pmd, 0));
        __flush_tlb_all();
 
        paravirt_release_pte(__pa(pte) >> PAGE_SHIFT);
        pte = newpte;
    }
    BUG_ON(vaddr < fix_to_virt(FIX_KMAP_BEGIN - 1)
           && vaddr > fix_to_virt(FIX_KMAP_END)
           && lastpte && lastpte + PTRS_PER_PTE != pte);
#endif
    return pte;
}

ここでは、ページテーブルの初期化の現在のアドレスが範囲の範囲内であり、そうであれば、そのコンテンツPTEのページ・テーブルがpage_table_range_init()アプリケーションにコピーされて入れ確認し、この一時的なカーネルマッピング間隔(FIX_KMAP_ENDとFIX_KMAP_BEGIN)に表示されるロゴを見ることができますない区間であれば、のみに参照することにより、ページ・テーブル・スペースは、キャッシュTLBはnewpte新しいページテーブルに対処リフレッシュし__flush_tlb_all()を呼び出し、その後(32ビットシステムは、実際にグローバルカタログページです)PMDに設定し、ページ・テーブル・スペースに割り当てられている()を呼び出しone_page_table_init。

これはpage_table_range_initを知ることを可能にする()主に何もしています。kmap_atomic()間隔を使用して、間隔は、連続ページ・テーブルを確保する必要があるためです。前固定マッピング領域の可能性を回避するために、これまでの一時的なカーネルマッピング範囲の要件に基づいて、ページテーブルの継続性を確保するために、ページテーブルエントリを割り当てられ、その原本の内容のこの再適用連続ページの表スペース・ページ・テーブルされています。この作品はマッピング領域コールset_fixmapの各固定部の関連するコードに引き渡されます後は、低メモリのページテーブルの初期化と異なっている、ということに注目すべきであるが、ここでは、ページテーブルが割り当てられているのです、対応するPTE項目初期化できません()関連ページにテーブル固定用の物理メモリマッピング領域に関連付けられています。

early_ioremap_page_table_range_init()関数、次いでearly_ioremap_resetダウン()は、グローバル変数の割り当てafter_paging_initあります。

最後の出口のearly_ioremap_page_table_range_init後()、init_mem_mapping()呼び出しload_cr3は()CR3レジスタを更新するには、__ flush_tlb_all()ので、TLBを更新し、新たなメモリページのマッピングを有効にするために使用されます。

この時点で、カーネルのページテーブルを設定します。

おすすめ

転載: www.cnblogs.com/linhaostudy/p/11621647.html