C/C++教程

arm64内存-paging_init-early_pgtable_alloc-fixmap pgd pud pmd pte idx

本文主要是介绍arm64内存-paging_init-early_pgtable_alloc-fixmap pgd pud pmd pte idx,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 

本文对应 https://www.cnblogs.com/zhangzhiwei122/p/16085238.html 中的“七、看到内存” 。

 

了解到了当前的物理内存的布局,但是内核仍然只是能够访问部分内存(kernel image mapping和DTB那两段内存,上图中黄色block),大部分的内存仍然处于黑暗中,等待光明的到来,也就是说需要创建这些内存的地址映射。

 

当前物理内存的布局的建立,见 前一篇: https://www.cnblogs.com/zhangzhiwei122/p/16086677.html

 

本文描述, 创建这些内存的地址映射。

 

arch/arm64/mm/mmu.c

paging_init

 709void __init paging_init(void)
 710{
 711        pgd_t *pgdp = pgd_set_fixmap(__pa_symbol(swapper_pg_dir));
 712
 713        map_kernel(pgdp);
 714        map_mem(pgdp);
 715
 716        pgd_clear_fixmap();
 717
 718        cpu_replace_ttbr1(lm_alias(swapper_pg_dir));
 719        init_mm.pgd = swapper_pg_dir;
 720
 721        memblock_free(__pa_symbol(init_pg_dir),
 722                      __pa_symbol(init_pg_end) - __pa_symbol(init_pg_dir));
 723
 724        memblock_allow_resize();
 725}

711 行,pgd_set_fixmap ,使用 fixmap 中的 pgd idx 对应的 虚拟地址 来映射   __pa_symbol(swapper_pg_dir)) 这个物理地址。 这个物理地址里面存放 pgd 页表。

713 行,同文件中的  static 函数, create fine-grained mappings for the kernel 。 即对 内核镜像 占用的区域,创建 属性合适的 (只读,读写、执行)映射。

714 行,同文件中的 static 函数, map_mem( pgd table pointer )

 472static void __init map_mem(pgd_t *pgdp)
 473{
 474        phys_addr_t kernel_start = __pa_symbol(_text);
 475        phys_addr_t kernel_end = __pa_symbol(__init_begin);
 476        phys_addr_t start, end;
 477        int flags = 0;
 478        u64 i;

 480        if (rodata_full || debug_pagealloc_enabled())
 481                flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
 482
 483        /*
 484         * Take care not to create a writable alias for the
 485         * read-only text and rodata sections of the kernel image.
 486         * So temporarily mark them as NOMAP to skip mappings in
 487         * the following for-loop
 488         */
 489        memblock_mark_nomap(kernel_start, kernel_end - kernel_start);

 495
 496        /* map all the memory banks */
 497        for_each_mem_range(i, &start, &end) {
 498                if (start >= end)
 499                        break;
 500                /*
 501                 * The linear map must allow allocation tags reading/writing
 502                 * if MTE is present. Otherwise, it has the same attributes as
 503                 * PAGE_KERNEL.
 504                 */
 505                __map_memblock(pgdp, start, end, PAGE_KERNEL_TAGGED, flags);
 506        }
 507
 508        /*
 509         * Map the linear alias of the [_text, __init_begin) interval
 510         * as non-executable now, and remove the write permission in
 511         * mark_linear_text_alias_ro() below (which will be called after
 512         * alternative patching has completed). This makes the contents
 513         * of the region accessible to subsystems such as hibernate,
 514         * but protects it from inadvertent modification or execution.
 515         * Note that contiguous mappings cannot be remapped in this way,
 516         * so we should avoid them here.
 517         */
 518        __map_memblock(pgdp, kernel_start, kernel_end,
 519                       PAGE_KERNEL, NO_CONT_MAPPINGS);
 520        memblock_clear_nomap(kernel_start, kernel_end - kernel_start);
 521

 536}
 537

kernel_start  ~ kernel_end 这段区域,需要 建立的页表属性为  read only ,其他的  页表属性为   通常 为 reading & writing  【 例外情况就是 480  ~ 481 行的, 配置了 readonly full 或者  debug page alloc 】

489 行,将 kernel_start  ~ kernel_end 这段区域 设置为 no map, 防止 下面的for 循环对其 建立映射页表。

497 ~ 506 行,对 memblock 中的内存,建立映射页表。flags 使用 0 或者 480 ~ 481 行设置的值。

518 行,对 kernel_start  ~ kernel_end 这段区域 建立页表,使用  NO_CONT_MAPPINGS

519 行,使用memblock 函数,clear no map ,清掉  489 行设置的 no map 属性。

 

使用到的函数,__map_memblock , 利用 early_pgtable_alloc 这个 辅助函数,建立页表。如下

 455static void __init __map_memblock(pgd_t *pgdp, phys_addr_t start,
 456                                  phys_addr_t end, pgprot_t prot, int flags)
 457{
 458        __create_pgd_mapping(pgdp, start, __phys_to_virt(start), end - start,
 459                             prot, early_pgtable_alloc, flags);
 460}

创建映射,需要 分配空间时,使用 early_pgtable_alloc 来分配。 这个函数也在 同文件中。

97 行,使用 memblock 函数,找到一页空闲的物理内存,地址是 phys

106 行,将97 行找到的物理地址,映射到 fixmap 总 pte idx 对应的 VA 地址处。

108 行,使用 memset 函数进行填充。

116 行返回物理地址。

 

  92static phys_addr_t __init early_pgtable_alloc(int shift)
  93{
  94        phys_addr_t phys;
  95        void *ptr;
  96
  97        phys = memblock_phys_alloc(PAGE_SIZE, PAGE_SIZE);
  98        if (!phys)
  99                panic("Failed to allocate page table page\n");
 100
 101        /*
 102         * The FIX_{PGD,PUD,PMD} slots may be in active use, but the FIX_PTE
 103         * slot will be free, so we can (ab)use the FIX_PTE slot to initialise
 104         * any level of table.
 105         */
 106        ptr = pte_set_fixmap(phys);
 107
 108        memset(ptr, 0, PAGE_SIZE);
 109
 110        /*
 111         * Implicit barriers also ensure the zeroed page is visible to the page
 112         * table walker
 113         */
 114        pte_clear_fixmap();
 115
 116        return phys;
 117}

背景说明:

1、fixmap 中,pgd pud pmd 对应的 虚拟地址可能正在被使用,但是 pte 对应的 虚拟地址  肯定是 可以被 覆盖的。

   因为 修改地址转换页表时,需要 使用 虚拟地址,因为已经使能了 MMU ,CPU 操作的地址都时VA 地址。

  但是 ,修改完成后,MMU工作时,就仅仅依赖页表里面填写的 物理地址了。

  所以,FIXMAP 中的 pte 这个 VA 地址,哪里需要就 映射到哪里,然后就 可以使用这个VA 地址,填充里面的值。

示例:

__create_pgd_mapping 传入的参数  pgd 页表,基地址  pgdir ,   查找到 页表 项  pgdp .  这个页表 项  VA 地址是可以访问的。

因为这个  pgdir 就是 paging_init 的 711 行,传入的  pgd_t *pgdp = pgd_set_fixmap(__pa_symbol(swapper_pg_dir));  fixmap 的 pgd idx 对应VA 映射到 swapper_pg_dir  PA 上面。

 349static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys,
 350                                 unsigned long virt, phys_addr_t size,
 351                                 pgprot_t prot,
 352                                 phys_addr_t (*pgtable_alloc)(int),
 353                                 int flags)
 354{
 355        unsigned long addr, end, next;
 356        pgd_t *pgdp = pgd_offset_pgd(pgdir, virt);

 369        do {
 371                alloc_init_pud(pgdp, addr, next, phys, prot, pgtable_alloc,
 372                               flags);
 374        } while (pgdp++, addr = next, addr != end);
 375}

由于 349 行传入的 pgdir 已经是  711 行建立过映射的,所以 356 行的 pgdp 可以被访问,这个地址在371行传入  alloc_init_pud 函数,在这个函数里面会 检查和 填充这个  pgdp 指针指向的  页表项 

 297
 298static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
 299                           phys_addr_t phys, pgprot_t prot,
 300                           phys_addr_t (*pgtable_alloc)(int),
 301                           int flags)
 302{
 303        unsigned long next;
 304        pud_t *pudp;
 305        p4d_t *p4dp = p4d_offset(pgdp, addr);
 306        p4d_t p4d = READ_ONCE(*p4dp);
 307
 308        if (p4d_none(p4d)) {
 311                pud_phys = pgtable_alloc(PUD_SHIFT);
 312                __p4d_populate(p4dp, pud_phys, PUD_TYPE_TABLE);
 313                p4d = READ_ONCE(*p4dp);
 314        }
 316
 317        pudp = pud_set_fixmap_offset(p4dp, addr);

 318        do {
 326                if (use_1G_block(addr, next, phys) &&
 336                } else {
 337                        alloc_init_cont_pmd(pudp, addr, next, phys, prot,
 338                                            pgtable_alloc, flags);
 339
 342                }
 344        } while (pudp++, addr = next, addr != end);
 345

305 行由 pgdp, addr 得到 p4dp , 可以简单理解为  p4dp = pgdp .

308 行就访问到 p4dp 这个地址了,幸好它在 711 行建立 了映射,所以现在可以被 read & check , 312 行,对其进行 write 操作。

312行,写入 p4dp 页表 项的内容是 pud 表 物理 起始地址,通过 pgtable_alloc 分配(也就是 92 行的 early_pgtable_alloc ,分配一页),物理地址填入 p4dp 页表项。

317 行, pud_set_fixmap_offset(p4dp, addr ) 这个函数,读出 p4dp 页表项里面的物理地址(312 行刚填写进入的),结合 addr,找到  pud 页表里面的 表项的物理地址,  然后将这个物理地址,映射到 fixmap 的 PUD IDX 对应的slot 上面

pudp 这个地址就是 映射后的 VA 地址了,可以访问,将它填入到  337 行的 alloc_init_cont_pmd 里面,在它里面,会 访问 pudp 这个指针指向的内存。

 249static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
 250                                unsigned long end, phys_addr_t phys,
 251                                pgprot_t prot,
 252                                phys_addr_t (*pgtable_alloc)(int), int flags)
 253{
 255        pud_t pud = READ_ONCE(*pudp);
 261        if (pud_none(pud)) {
 264                pmd_phys = pgtable_alloc(PMD_SHIFT);
 265                __pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE);
 267        }
 270        do {
 276                if ((((addr | next | phys) & ~CONT_PMD_MASK) == 0) &&
 277                    (flags & NO_CONT_MAPPINGS) == 0)
 280                init_pmd(pudp, addr, next, phys, __prot, pgtable_alloc, flags);
 281
 282                phys += next - addr;
 283        } while (addr = next, addr != end);
 284}

255 行就 read 这个地址,读出 页表项 内容,pud

261 行检查,如果为空,就在264 行申请 物理地址,然后265 行,写入 pud 页表 表项里面。

280 行,继续将 pudp 指针,传递给  init_pmd 函数。

 212static void init_pmd(pud_t *pudp, unsigned long addr, unsigned long end,
 213                     phys_addr_t phys, pgprot_t prot,
 214                     phys_addr_t (*pgtable_alloc)(int), int flags)
 215{
 219        pmdp = pmd_set_fixmap_offset(pudp, addr);
 220        do {
 221                pmd_t old_pmd = READ_ONCE(*pmdp);
 222
 226                if (((addr | next | phys) & ~SECTION_MASK) == 0 &&
 227                    (flags & NO_BLOCK_MAPPINGS) == 0) {
 236                } else {
 237                        alloc_init_cont_pte(pmdp, addr, next, phys, prot,
 238                                            pgtable_alloc, flags);
 242                }
 243                phys += next - addr;
 244        } while (pmdp++, addr = next, addr != end);
 247}
 248

219 行,pmd_set_fixmap_offset 函数,使用 pudp 页表项里面的物理地址,前面 264 行分配,265行填入的,结合 addr,找到 pmd 页表 里面对应的 表项 的物理地址,然后将这个物理地址映射到 fixmap 的 PMD IDX slot 对应的 VA 地址上面

221 就立刻使用了 219 行映射的 VA 地址, read once .

237 行,将映射后的VA 地址,交给  alloc_init_cont_pte 函数,它里面会访问  pmdp 这个VA 地址,读,检查,写,类似 249 行, alloc_init_cont_pmd 函数里面。

 177static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
 178                                unsigned long end, phys_addr_t phys,
 179                                pgprot_t prot,
 180                                phys_addr_t (*pgtable_alloc)(int),
 181                                int flags)
 182{
 184        pmd_t pmd = READ_ONCE(*pmdp);
 185
 187        if (pmd_none(pmd)) {
 190                pte_phys = pgtable_alloc(PAGE_SHIFT);
 191                __pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
 192                pmd = READ_ONCE(*pmdp);
 193        }
 195
 196        do {
 202                if ((((addr | next | phys) & ~CONT_PTE_MASK) == 0) &&
 203                    (flags & NO_CONT_MAPPINGS) == 0)
 206                init_pte(pmdp, addr, next, phys, __prot);
 207
 209        } while (addr = next, addr != end);
 210}

 

类似249 行, alloc_init_cont_pmd 函数,187 ~ 193 行,检查,如果为空,就设置。206 行,将pmdp VA地址传入 init_pte 函数

 153static void init_pte(pmd_t *pmdp, unsigned long addr, unsigned long end,
 154                     phys_addr_t phys, pgprot_t prot)
 155{
 158        ptep = pte_set_fixmap_offset(pmdp, addr);
 159        do {
 162                set_pte(ptep, pfn_pte(__phys_to_pfn(phys), prot));
 163
 172        } while (ptep++, addr += PAGE_SIZE, addr != end);
 173
 174        pte_clear_fixmap();
 175}
 

158 行,类似前面 219 行。 从pmdp 页表 项中取出物理地址,这个物理地址时 191 行填入,然后依据 addr 找到  pte 页表中,表项的 物理地址,然后将物理地址,映射到 fixmap 的 PTE IDX slot 上面

163 行,就使用刚才映射的  ptep VA 地址。

 

这样,使用fixmap 的 pgd  pud pmp  pte 4个 slot ,和 early_pgtable_alloc   函数(使用 memblock 分 一页 物理地址), 就可以 对 memblock 中所有 记录的 内存进行映射表的创建和填写了。

 

这篇关于arm64内存-paging_init-early_pgtable_alloc-fixmap pgd pud pmd pte idx的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!