linux bootmem (5)

C代码阶段

在arm64汇编阶段完成了kernel 代码段(应该是含有data和bss部分)的页表映射、内存属性的初始化和mmu的使能,内核栈和其它的cpu功能的初始化。下面正式介绍bootmem内存分配器。

bootmem初始化

start_kernel -->setup_arch

                   --> early_fixmap_init

                   --> early_ioremap_init—> early_ioremap_setup

                   à setup_machine_fdt

                   --> arm64_memblock_init();

                   --> paging_init

-->bootmem_init

下面依次介绍setup_arch函数中和内存相关的几个函数

early_fixmap_init

为FIXADDR_START建立页表映射,下面是固定映射部分分布图

https://img2018.cnblogs.com/blog/1771657/201908/1771657-20190831230833457-192209033.png

从分布图上看,要想使用fdt和io空间,必须先要初始化这部分页表。在系统解析fdt之前,尚不知道内存ddr的分布信息,都依赖于编译时的地址。所以要尽快解析fdt获得内存信息。

fixed map是被linux kernel用来解决如下问题:

1)在很早期的阶段需要进行地址映射,而此时,由于内存管理模块还没有完成初始化,不能动态分配内存,也就是无法动态分配创建映射需要的页表内存空间。

2)物理地址是固定的,或者是在运行时就可以确定的。对于这类问题,内核定义了一段固定映射的虚拟地址,让使用fix map机制的各个模块可以在系统启动的早期就可以创建地址映射,当然,这种机制不是那么灵活,因为虚拟地址都是编译时固定分配的。

enum fixed_addresses {

         FIX_HOLE,

#define FIX_FDT_SIZE           (MAX_FDT_SIZE + SZ_2M)

         FIX_FDT_END,

         FIX_FDT = FIX_FDT_END + FIX_FDT_SIZE / PAGE_SIZE - 1,

         FIX_EARLYCON_MEM_BASE,

         FIX_TEXT_POKE0,

         __end_of_permanent_fixed_addresses,

#define NR_FIX_BTMAPS               (SZ_256K / PAGE_SIZE)

#define FIX_BTMAPS_SLOTS         7

#define TOTAL_FIX_BTMAPS         (NR_FIX_BTMAPS * FIX_BTMAPS_SLOTS)

         FIX_BTMAP_END = __end_of_permanent_fixed_addresses,

         FIX_BTMAP_BEGIN = FIX_BTMAP_END + TOTAL_FIX_BTMAPS - 1,

         FIX_PTE,

         FIX_PMD,

         FIX_PUD,

         FIX_PGD,

         __end_of_fixed_addresses

};

 

PGD当然就是复用swapper进程的PGD了(其实整个系统就一个PGD)

void __init early_fixmap_init(void)

{

         pgd_t *pgd;

         pud_t *pud;

         pmd_t *pmd;

         unsigned long addr = FIXADDR_START; 这段空间的开始地址(虚拟地址)

 

         pgd = pgd_offset_k(addr); 确定addr在页表中的偏移

#define pgd_offset_k(addr)  pgd_offset(&init_mm, addr)

#define pgd_offset(mm, addr)      (pgd_offset_raw((mm)->pgd, (addr)))

#define pgd_offset_raw(pgd, addr)        ((pgd) + pgd_index(addr)) 基地址+偏移量

#define pgd_index(addr)                (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1)) 计算偏移量

    if (CONFIG_PGTABLE_LEVELS > 3 && !(pgd_none(*pgd) || pgd_page_paddr(*pgd) == __pa_symbol(bm_pud))) {

                   BUG_ON(!IS_ENABLED(CONFIG_ARM64_16K_PAGES));

                   pud = pud_offset_kimg(pgd, addr); 确定addrpud中的偏移

         } else {

                   if (pgd_none(*pgd))

                            __pgd_populate(pgd, __pa_symbol(bm_pud), PUD_TYPE_TABLE); 填充PGD页表项

static pte_t bm_pte[PTRS_PER_PTE] __page_aligned_bss;

static pmd_t bm_pmd[PTRS_PER_PMD] __page_aligned_bss __maybe_unused;

static pud_t bm_pud[PTRS_PER_PUD] __page_aligned_bss __maybe_unused;

全局数组作为pud/pmd/pte页表地址

#define PTRS_PER_PTE                  (1 << (PAGE_SHIFT - 3))  1<<9 512,其它也都是512

linuxarm64页表有一个“隐形”规定,即各级页表的有效bit数比页面bit数小3。举例说4KB的页面其bit数为12,那么level0~level3的有效bit数为12-3=9,这样虚拟地址有效bit48时,则4x9+12=48

static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud)

{

         __pgd_populate(pgd, __pa(pud), PUD_TYPE_TABLE);

}

static inline void __pgd_populate(pgd_t *pgdp, phys_addr_t pud, pgdval_t prot)

{

         set_pgd(pgdp, __pgd(pud | prot));  pud地址和pud属性作为pgd的页表项

}

static inline void set_pgd(pgd_t *pgdp, pgd_t pgd)

{

         *pgdp = pgd;

         dsb(ishst);

}

#define __pgd(x) ((pgd_t) { (x) } )

                   pud = fixmap_pud(addr);

         }

         pgd类似,依次创建pudpmd的页表项

         if (pud_none(*pud))

                   __pud_populate(pud, __pa_symbol(bm_pmd), PMD_TYPE_TABLE);

         pmd = fixmap_pmd(addr);

         __pmd_populate(pmd, __pa_symbol(bm_pte), PMD_TYPE_TABLE); pmd依然是table描述符

         下面没有创建pte,说明有具体使用时再创建

         BUILD_BUG_ON((__fix_to_virt(FIX_BTMAP_BEGIN) >> PMD_SHIFT)

                        != (__fix_to_virt(FIX_BTMAP_END) >> PMD_SHIFT));需要在同一个pmd页表项内,为2MB

[FIX_BTMAP_BEGIN, FIX_BTMAP_END]区域给动态映射使用,保证该区域正好位于[addr - 2M, addr]之间,必须检查动态映射区域小于2M。定义的PTE页表数组其实是给动态映射使用的。当我们需要访问物理地址A,从[addr - 2M, addr]区域找到一个合适的虚拟地址B,填充B地址对应的PTE页表项即可访问。这也是early_ioremap的实现原理。

简单理解,fix_bitmap是不能使用ioremap 创建io映射时的临时解决方案

         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);

         }

}

 

early_ioremap_setup

early ioremap利用slot管理映射,最多支持FIX_BTMAPS_SLOTS==7个映射,每个映射最大支持映射256KB。slot_virt数组存储每个slot的虚拟地址首地址。prev_map数组用来记录已经分配出去的虚拟地址,数组值为0代表没有分配。prev_size记录映射的size。

static void __iomem *prev_map[FIX_BTMAPS_SLOTS] __initdata;

static unsigned long prev_size[FIX_BTMAPS_SLOTS] __initdata;

static unsigned long slot_virt[FIX_BTMAPS_SLOTS] __initdata;

void __init early_ioremap_setup(void)

{

         int i;

         for (i = 0; i < FIX_BTMAPS_SLOTS; i++)

                   if (WARN_ON(prev_map[i]))

                            break;

         for (i = 0; i < FIX_BTMAPS_SLOTS; i++)

                   slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i);

}

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值