linux arm64 early_fixmap_init 分析记录

Linux4.0,运行环境qemu,arm64平台

void __init early_fixmap_init(void)        // 为FIXADDR_START建立页表映射                   
{                                                             
    pgd_t *pgd;                                               
    pud_t *pud;                                               
    pmd_t *pmd;                                               
    unsigned long addr = FIXADDR_START;     // 0xffff7ffffabfe000                  
                                                              
    pgd = pgd_offset_k(addr);    // addr = 0xffff7ffffabfe000 pgd = 0xffff800000eec7f8                  
    // 此处 bm_pud 为静态定义的 pud_t 变量,此处定义的未初始化的全局变量,存放在bss段中,
    // pgd 为 FIXADDR = 0xffff7ffffabfe000 虚拟地址,在init进程的pgd全局页表项中的页表项的虚拟地址
    // 访问pgd虚拟地址,即可得到pud的物理基地址
    // 此函数即将bm_pud变量的虚拟地址对应的物理基地址,然后此物理基地址或上PUD_TYPE_TABLE,将此结果
    // 存放到 FIXADDR_START所对应的pgd页表中,即 pgd_val(pgd) = 此物理地址
    pgd_populate(&init_mm, pgd, bm_pud);   
                       
    pud = pud_offset(pgd, addr);                              
    pud_populate(&init_mm, pud, bm_pmd);                      
    pmd = pmd_offset(pud, addr);                              
    pmd_populate_kernel(&init_mm, pmd, bm_pte);               
                                                              
    /*                                                        
     * The boot-ioremap range spans multiple pmds, for which  
     * we are not preparted:                                  
     */                                                       
    BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)
        ¦    != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT)); 
                                                              
    if ((pmd != fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)))     
        ¦|| pmd != fixmap_pmd(fix_to_virt(FIX_BTMAP_END))) {  
        WARN_ON(1);                                           
        pr_warn("pmd %p != %p, %p\n",                         
            pmd, fixmap_pmd(fix_to_virt(FIX_BTMAP_BEGIN)),    
            fixmap_pmd(fix_to_virt(FIX_BTMAP_END)));          
        pr_warn("fix_to_virt(FIX_BTMAP_BEGIN): %08lx\n",      
            fix_to_virt(FIX_BTMAP_BEGIN));                    
        pr_warn("fix_to_virt(FIX_BTMAP_END):   %08lx\n",      
            fix_to_virt(FIX_BTMAP_END));                      
                                                              
        pr_warn("FIX_BTMAP_END:       %d\n", FIX_BTMAP_END);  
        pr_warn("FIX_BTMAP_BEGIN:     %d\n", FIX_BTMAP_BEGIN);
    }                                                         
}                                                             

                                                                    val                            页表项        页表项物理地址
FIXADDR_START  = 0xffff7ffffabfe000                                            
swapper_pg_dir      = 0xffff800000ef0000                                                                    
pgd                         = 0xffff800000ef07f8        0x40ebd003        bm_pud        0x40ebd000
pud                         = 0xffff800000ebdff8        0x40ebc003        bm_pmd        0x40ebc000
pmd                        = 0xffff800000ebcea8        0x40ebb003        bm_pte        0x40ebb000


虚拟地址 0xffff800000ef07f8 对应的物理地址 0x40ef07f8 处,存放的就是 bm_pud 的物理基地址
可以执行命令:
    / # devmem 0x40ef07f8 64
        0x0000000040EBD003
可以访问到物理内存 0x40ef07f8 ,内容为0x0000000040EBD003

/ # devmem 0x40ebdff8 64
0x0000000040EBC003

/ # devmem 0x40ebcea8 64
0x0000000040EBB003

一.PGD页表的处理

/* pgd_offset */

1.首先,对于pgd页表,会通过目标虚拟地址addr(此处为addr = FIXADDR_START= 0xffff7ffffabfe000),计算出此addr在pgd页表项中的偏移offset,然后通过进程的pgd的基地址加上offset*8,注(为什么乘以8?因为arm64 一个pgd页表项包含8个字节),即可得到此addr在全局页表项中的那个pgd页表的虚拟地址 pgd_vaddr

2.得到pgd_vaddr后,可以在此pgd_vaddr处填充 pud的物理基地址

注:内核中静态定义了 bm_pud[512] __page_aligned_bss,bm_pmd[512] __page_aligned_bss,

bm_pte[512] __page_aligned_bss,因为静态定义的全局未初始化的变量,存在于bss段中,所以定义后,便在bss段中有地址
静态数组         物理地址                  虚拟地址
bm_pud        0x40ebd000        0xffff800000ebd000
bm_pmd       0x40ebc000        0xffff800000ebc000
bm_pte         0x40ebb000        0xffff800000ebb000

3.代码讲解:

3.1 start_kernele -> setup_arch -> early_fixmap_init -> pgd = pgd_offset_k(addr)

      pgd_offset_k(addr)函数,以init进程中的pgd页表基地址为pgd的基地址,init进程中的pgd即为 swapper_pg_dir=0xffff800000ef0000

      知道了pgd基地址,那么需要知道addr在pgd页表中的哪一项,需要跟进addr计算在pgd页表中的offset,跟进虚拟地址的组成,可知,pgd的索引位为: addr[47:39],占据9位,所以计算方式为:(addr >> 39) & (2^9 - 1) = (0xffff7ffffabfe000>>39)&0x1ff = 0xff,则在pgd中的偏移为0xff,因为一个pgd页表项有8个字节,所以0xff个页表项的实际偏移为:0xff*8 = 0x7f8

则addr在gpd页表中的pgd的虚拟地址为:0xffff800000ef0000 + 0x7f8 = 0xffff800000ef07f8

3.2 start_kernele -> setup_arch -> early_fixmap_init -> pgd_populate(&init_mm, pgd, bm_pud);

pgd_populate(&init_mm,pgd,bm_pud)函数,将pud页表的物理基地址放到 addr对应的pgd页表项对应的地址中去,由于bm_pud的虚拟地址为0xffff800000ebd000,物理地址为0x40ebd000,所以将 0x40ebd000 (另外还需要或上PUD_TABLE的标记)值,放到0xffff800000ef07f8这个虚拟地址对应的物理地址上去,即pgd页表项中,存放了pud页表的物理基地址

此函数调用了set_pgd(pgd, __pgd(__pa(pud) | PUD_TYPE_TABLE));

__pa(pud)即为 __pa(bm_pud),即将0xffff800000ebd000虚拟地址转换为物理地址,由于是线性映射的,所以为:

0xffff800000ebd000 - 0xffff800000000000 + 0x40000000 = 0x40ebd000

PUD_TYPE_TABLE = 3,所以__pa(pud) | PUD_TYPE_TABLE = 0x40ebd000 | 0x3 = 0x40ebd003

__pgd(x)宏是构造一个pgd变量,set_pgd函数将新构造的pgd变量,指向pgd_populate函数传进来的gpd并返回,此时调用pgd_populate(&init_mm, pgd, bm_pud)函数后,pgd_val便为pud的物理基地址0x40ebd003

二.PUD页表的处理

同理,简单记录PUD,PMD的过程

pud = pud_offset(pgd, addr); // pgd = ffff800000ef07f8     //addr = 0xffff7ffffabfe000

接着调用:(pud_t *)pgd_page_vaddr(*pgd) + pud_index(addr)

pud_index(addr) = (0xffff7ffffabfe000 >> 30)& 0x1ff = 0x1ff

pgd_page_vaddr(*pgd) = pgd_page_vaddr(*pgd) = __va(pgd_val(pgd) & PHYS_MASK & (s32)PAGE_MASK);

pgd_val(pgd) = 0x40ebd003

PHYS_MASK  = 0xffffffffffff

(s32)PAGE_MASK = 0xfffff000

所以__va(0x40ebd003 & 0xffffffffffff & 0xfffff000) = __va(0x40ebd000) = 0xffff800000ebd000

所以最终 (pud_t *)pgd_page_vaddr(*pgd) + pud_index(addr) 

             = 0xffff800000ebd000 + 0x1ff*8 = 0xffff800000ebdff8

所以pud = 0xffff800000ebdff8

pud_populate函数和pgd_populate完全类似,执行完后,pud页表项中存放的是bm_pmd的物理基地址

 

三.PMD页表的处理

同理不再介绍

最后,没有建立PTE页表项,此处放在使用的地方进行映射

你可能感兴趣的:(arm64,arm64,linux)