3.5.1.1. add_static_vm_early

void __init add_static_vm_early(struct static_vm *svm)
{
    struct static_vm *curr_svm;
    struct vm_struct *vm;
    void *vaddr;

    vm = &svm->vm;
    vm_area_add_early(vm);
    vaddr = vm->addr;

    list_for_each_entry(curr_svm, &static_vmlist, list) {
        vm = &curr_svm->vm;
        if(vm->addr > vaddr)
            break;
    }
    list_add_tail(&svm->list, &curr_svm->list);
}

add_static_vm_early 函数用于内核启动早期,将一个static_vm加入到内核维护的vmlist和static_vmlist里.函数首先获得参数svm对应的vm,并通过函数 vm_area_add_early函数将该vm加入到系统早期的vmlist里.函数接着使用list_for_each_entry函数遍历系统的static_vmlist链表,直到链表中找到一个成员的 地址大于svm对应的地址,然后调用list_add_tail函数添加到链表的当前位置

3.5.1.2. adjust_lowmem_bounds

void __init adjust_lowmem_bounds(void)
{
    phys_addr_t memblock_limit = 0;
    u64 vmalloc_limit;
    struct memblock_region *reg;
    phys_addr_t lowmem_limit = 0;

    vmaloc_limit = (u64)(uintptr_t)vmalloc_min - PAGE_OFFSET + PHYS_OFFSET;

    //遍历MEMBLOCK内存分配器的memory可用物理内存区域内的所有regions
    for_each_memblock(memory, reg) {
        //每遍历一块内存区,获取该内存区的起始和终止地址
        phys_addr_t block_start = reg->base;
        phys_addr_t block_end = reg->base + reg->size;

        //如果该内存区的地址小于vmalloc_limit
        if(reg->base < vmalloc_limit) {
            if(block_end > lowmem_limit)
                lowmem_limit = min_t(u64, vmalloc_limit, block_end);

                //进行内存对齐
                if(!memblock_limit) {
                    if(!IS_ALIGNED(block_start, PMG_SIZE))
                        memblock_limit = block_start;
                    else if(!IS_ALIGNED(block_end, PMD_SIZE))
                        memblock_limit = lowmem_limit;
                }
        }
    }
    arm_lowmem_limit = lowmem_limit;
    high_memory = __va(arm_lowmem_limit - 1) + 1;

    if(!memblock_limit)
        memblock_limit = arm_lowmem_limit;

    memblock_limit = round_down(memblock_limit, PMD_SIZE);

    if(!IS_ENABLED(CONFIG_HIGHMEM) || cache_is_vipt_aliasing()) {
        if(memblock_end_of_DRAM() > arm_lowmem_limit) {
            phys_addr_t end = memblock_end_of_DRAM();

            pr_notice("Ignoring RAM at %pa-%pa\n", &memblock_limit, &end);
            pr_notice("Consider using a HIGHMEM enabled kernel.\n");

            memblock_remove(memblock_limit, end - memblock_limit);
        }
    }

    memblock_set_current_limit(memblock_limit);
}

adjust_lowmem_bounds 用于调整获得低端物理内存的边界,该函数主要在内存初始化阶段调用,用于确定可用的低端内存范围.

在linux内核中,物理内存被分为两个区域: 低端内存高端内存 .低端内存是位于物理地址较低的区域,通常包含操作系统内核和常用的数据结构, 例如页表,任务状态段等.高端内存则是位于物理地址较高的区域,用于存储用户进程的堆栈,缓冲区等

vmaloc_limit = (u64)(uintptr_t)vmalloc_min - PAGE_OFFSET + PHYS_OFFSET;

vmaloc_limit用于存储vmalloc内存区的极值,lowmem_limit用于存储低端物理内存的限值

注解

  • vmalloc_min: VMALLOC内存区的最低地址

  • PAGE_OFFSET: 内核虚拟地址的起始地址

  • PHYS_OFFSET: DRAM在地址总线行的偏移,即DRAM的物理地址

3.5.1.3. alloc_init_pmd

static void __init alloc_init_pwd(pud_t *pud, unsigned long addr, unsigned long end,
                    phys_addr_t phys, cosnt struct mem_type *type,
                    void *(alloc)(unsigned long sz), bool ng)
{
    pmd_t *pmd = pmd_offset(pud, addr);
    unsigned long next;

    do {
        //获取addr对应的下一个PMD入口
        next = pmd_addr_end(addr, end);

        if(type->port_sect &&
            ((addr | next | phys) & ~SECTION_MASK) == 0)
            //初始化PMD入口项
            __map_init_section(pmd, addr, next, phys, type, ng);
        else
            //反之则分配一个pte页并初始化PMD入口项
            alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys), type, alloc, ng);

        //增加phys和pmd的值
        phys += next - addr;

    } while(pmd++, addr = next, addr != end);
}

alloc_init_pmd 用于分配或初始化一个页表中间级页表(PMD)入口

  • pud: 指向PUD入口

  • addr: 虚拟地址

  • end: 结束虚拟地址

  • phys: 指向物理地址

  • type: 指向内存类型

  • alloc: 指向分配函数

3.5.1.4. alloc_init_pud

static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
    unsigned long end, phys_addr_t phys, const struct mem_type,
    void *(*alloc)(unsigned long sz), bool ng)
{
    pud_t *pud = pud_offset(pgd, addr);

    do {
        next = pud_addr_end(addr, end);
        alloc_init_pmd(pud, addr, next, phys, type, alloc, ng);
        phys += next - addr;
    } while(pud++, addr = next, addr != end);
}

alloc_init_pud 用于分配并初始化一个页表顶级页表(PUD)的条目

注解

页表是用于虚拟地址到物理地址的映射的数据结构. 页表层次结构包含多个级别,其中最顶级的页表是页表顶级页表(PUD)

  • PGD(Page Global Directory): 是页表的最顶层目录,用于建立虚拟地址到物理地址的映射关系.每个PGD条目对应一个PGD表,其中包含多个PUD条目

  • PUD(Page Upper Directory): 是页表的第二级目录,用于映射较大范围的虚拟地址空间,每个PUD条目对应一个PUD表,其中包含多个PMD条目

  • PMD(Page Middle Directory): 是页表的第三级目录,用于映射较中等范围的虚拟地址空间,每个PMD条目对应一个PMD表,其中包含多个页表项

  • PT(Page Table): 是页表的最底层目录,用于映射较小范围的虚拟地址空间,每个PT条目对应一个物理页面,用于存储页表项

+---------------------------------------+
|                PGD                    |
|                                       |
|          +------------------------+   |
|          |          PUD           |   |
|          |                        |   |
|          |   +----------------+   |   |
|          |   |      PMD       |   |   |
|          |   |                |   |   |
|          |   |   +--------+   |   |   |
|          |   |   |   PT   |   |   |   |
|          |   |   +--------+   |   |   |
|          |   |                |   |   |
|          |   +----------------+   |   |
|          |                        |   |
|          +------------------------+   |
|                                       |
+---------------------------------------+

3.5.1.5. alloc_node_mem_map

static void __ref alloc_node_mem_map(struct pglist_data *pgdat)
{
    unsigned long __maybe_unused start = 0;
    unsigned long __maybe_unused offset = 0;

    //判断pgdat对应的节点是否包含物理页帧
    if(!pgdat->node_spanned_pages)
        return;

    //进行MAX_ORDER_NR_PAGES对齐
    start = pgdat->node_start_pfn & ~ (MAX_ORDER_NR_PAGES - 1);
    offset = pgdate->node_start_pfn - start;

    if(!pgdat->node_mem_map) {
        unsigned long size, end;
        struct page *map;

        end = pgdat_end_pfn(pgdat);
        end = ALIGN(end, MAX_ORDER_NR_PAGES);
        size = (end - start) * sizeof(struct page);
        map = memblock_alloc_node_nopanic(size, pgdat->node_id);
        pgdat->node_mem_map = map + offset;
    }
    #ifndef CONFIG_NEED_MULTIPLE_NODES
    if(pgdat == NODE_DATA(0)) {
        mem_map = NODE_DATA(0)->node_mem_map;
    #if defined(CONFIG_HAVE_MEMBLOCK_NODE_MAPE) || defined(CONFIG_FLATMEM)
        if(papge_to_pfn(mem_map) != pgdat->node_start_pfn)
            mem_map -= offset;
    #endif
    }
    #endif
}

alloc_nod_mem_map 用于创建全局struct page数组mem_map,并将mem_map与节点0的物理页帧进行映射

3.5.1.6. ARRAY_SIZE

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

用于计算数组中成员的个数

3.5.1.7. arch_get_next_mach

static const void * __init arch_get_next_mach(const char *const **match)
{
    static const struct machine_desc *mdesc = __arch_info_begin;
    const struct machine_desc *m = mdesc;

    if(m >= __arch_info_end)
        return NULL;

    mdesc++;
    *match = m->dt_compat;
    return m;
}

arch_get_next_mach 用于获得下一个machine_desc结构的device tree compatible strings地址.

3.5.1.8. arch_local_irq_disable

static inline void arch_local_irq_disable(void)
{
    asm_volatile(
        "   cpsid i @ arch_local_irq_disable"
        :
        :
        : "memory", "cc");
}

在ARMV7版本中通过arch_local_irq_disable函数实现禁止本地中断,CPSID指令与I参数用该与将CPSR寄存器中的interrupt标志位清零,以此禁止本地中断

3.5.1.9. arm_adjust_dma_zone

static void __init arm_adjust_dma_zone(unsigned long *size, unsigned long *hole,
            unsigned long dma_size)
{
    if(size[0] <= dma_size)
        return;

    size[ZONE_NORMAL] = size[0] - dma_size;
    size[ZONE_DMA] = dma_size;
    hole[ZONE_NORMAL] = hole[0];
    holo[ZONE_DMA] = 0;
}

arm_adjust_dma_zone 用于调整DMA_ZONE和NORMAL_ZONE统计数组

  • size: 指向可用物理页帧数组

  • hole: 指向碎片物理页帧数组

  • dma_size: 指向DMA_ZONE占用的物理页帧

3.5.1.10. arm_initrd_init

static void __init arm_initrd_init(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
    phys_addr_t start;
    unsigned long size;

    initrd_start = initrd_end = 0;

    if(!phys_initrd_size)
        return;

    //对start和size进行PAGE_SIZE对齐操作
    start = round_down(phys_initrd_start, PAGE_SIZE);
    size = phys_initrd_size + (phys_initrd_start - start);
    size = round_up(size, PAGE_SIZE);

    //判断start和size对应的内存区域是否在MEMBLOCK的reserved和memory区域内
    if(!memblock_is_region_memory(start, size))
        return;

    if(memblock_is_region_reserved(start, size))
        return;

    //通过检查后,将start和size对应的区域加入到MEMBLOCK的预留区域内
    memblock_reserve(start, size);

    //并将phys_initd_start对应的虚拟地址赋值给initd_start
    initrd_start = __phys_to_virt(phys_initrd_start);
    initrd_end = initrd_start + phys_initrd_size;

#endif
}

arm_initrd_init 将initrd所占用的物理内存加入到memblock内存分配器的预留区内

注解

系统启动过程中,uboot将initrd占用的物理地址信息存储在dtb的chosen节点内,在early_init_dt_check_for_initrd函数中, 从dtb中解析出inird的物理信息存储在phy_initrd_start和phys_initrd_size两个全局变量里

3.5.1.11. arm_pte_alloc

static pte_t * __init arm_pte_alloc(pmd_t *pmd, unsigned long addr,
            unsigned long prot, void *(*alloc)(unsigned long sz))
{
    //判断pmd是否为空
    if(pmd_none(*pmd)) {
        //使用alloc函数进行内存分配,长度为PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE
        pte_t *pte = alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);
        //将PTE填充到PMD入口项里
        __pmd_populate(pmd, __pa(pte), prot);
    }

    //检测pmd入口项的有效性
    BUG_ON(pmd_bad(*pmd));
    //返回addr虚拟地址对应的pte入口
    return pte_offset_kernel(pmd, addr);
}

arm_pte_alloc 用于分配并安装一个新的PTE页表

  • pmd: 指向PMD入口

  • addr: 指向虚拟地址

  • prot: 指向页表属性

  • alloc: 执行内存分配函数

3.5.1.12. arm_memblock_init

void __init arm_memblock_init(const struct machine_desc *mdesc)
{
    //将内核镜像所在的物理块加入到保留区
    memblock_reserved(__pa(KERNEL_START), KERNEL_END - KERNEL_START);

    //将INITRD所占用的物理内存区加入到保留区
    arm_initrd_init();

    //将内核全局页目录所在的物理地址加入到保留区内
    arm_mm_memblock_reserve();

    //将dtb memory reserved mapping中指定的区域加入到保留区
    if(mdesc->reserve)
        mdesc->reserve();

    early_init_fdt_reserve_self();
    early_init_fdt_scan_reserved_mem();

    dma_contiguous_reserve(arm_dma_limit);

    arm_memblock_steal_permitted = false;
    memblock_dump_all();
}

arm_memblock_init 用于将系统需要预留的内存加入到MEMBLOCK内存分配器的预留区域中

3.5.1.13. arm_mm_memblock_reserve

void __init arm_mm_memblock_reserve(void)
{
    memblock_reserve(__pa(swapper_pg_dir), SWAPPER_PG_DIR_SIZE);

    #ifdef CONFIG_SA1111
    memblock_reserve(PHYS_OFFSET, __pa(swapper_pg_dir) - PHYS_OFFSET);
    #endif
}