奔跑吧linux内核-物理内存初始化

时间:2021-10-24 21:40:17

内存管理是一个很复杂的系统,涉及的内容很多,下图是一个抽象的概述
奔跑吧linux内核-物理内存初始化

内存大小

在ARM linux中用dts(dts由powerPC的fdt演变过来)呈现,在ARM vexpress中,内存的定义是在
kernel\linux\v4.4\arch\arm\boot\dts\vexpress-v2p-ca9.dts中,内存的起始地址是0x60000000,大小为0x40000000即1G大小

    memory@60000000 {
        device_type = "memory";
        reg = <0x60000000 0x40000000>;
    };

内核启动时解析dts,实现的代码在drivers/of/fdt.c
start_kernel->setup_arch(&command_line)->setup_machine_fdt(__atags_pointer)->early_init_dt_scan_nodes->[of_scan_flat_dt(early_init_dt_scan_memory, NULL)]
->early_init_dt_scan_memory

/** * early_init_dt_scan_memory - Look for an parse memory nodes */
int __init early_init_dt_scan_memory(unsigned long node, const char *uname,
                     int depth, void *data)
{
    const char *type = of_get_flat_dt_prop(node, "device_type", NULL);
    const __be32 *reg, *endp;
    int l;

    /* We are scanning "memory" nodes only */
    if (type == NULL) {
        /* * The longtrail doesn't have a device_type on the * /memory node, so look for the node called /memory@0. */
        if (!IS_ENABLED(CONFIG_PPC32) || depth != 1 || strcmp(uname, "memory@0") != 0)
            return 0;
    } else if (strcmp(type, "memory") != 0)
        return 0;

    reg = of_get_flat_dt_prop(node, "linux,usable-memory", &l);
    if (reg == NULL)
        reg = of_get_flat_dt_prop(node, "reg", &l);
    if (reg == NULL)
        return 0;

    endp = reg + (l / sizeof(__be32));

    pr_debug("memory scan node %s, reg size %d,\n", uname, l);

    while ((endp - reg) >= (dt_root_addr_cells + dt_root_size_cells)) {
        u64 base, size;

        base = dt_mem_next_cell(dt_root_addr_cells, &reg);
        size = dt_mem_next_cell(dt_root_size_cells, &reg);

        if (size == 0)
            continue;
        pr_debug(" - %llx , %llx\n", (unsigned long long)base,
            (unsigned long long)size);

        early_init_dt_add_memory_arch(base, size);
    }

    return 0;
}

获取从dts中memory节点得到内存的base_address和size信息最后通过early_init_dt_add_memory_arch(base, size)调用memblock_add加入到memblock子系统

物理内存映射

内核使用内存前,需要初始化内核的页表,初始化页表只要在map_lowmem函数中,且在映射页表之前,需要把页表清零,主要在prepare_page_table函数中实现

static inline void prepare_page_table(void)
{
    unsigned long addr;
    phys_addr_t end;

    /*
     * Clear out all the mappings below the kernel image.
     */
    for (addr = 0; addr < MODULES_VADDR; addr += PMD_SIZE)
        pmd_clear(pmd_off_k(addr));

#ifdef CONFIG_XIP_KERNEL
    /* The XIP kernel is mapped in the module area -- skip over it */
    addr = ((unsigned long)_etext + PMD_SIZE - 1) & PMD_MASK;
#endif
    for ( ; addr < PAGE_OFFSET; addr += PMD_SIZE)
        pmd_clear(pmd_off_k(addr));

    /*
     * Find the end of the first block of lowmem.
     */
    end = memblock.memory.regions[0].base + memblock.memory.regions[0].size;
    if (end >= arm_lowmem_limit)
        end = arm_lowmem_limit;

    /*
     * Clear out all the kernel space mappings, except for the first
     * memory bank, up to the vmalloc region.
     */
    for (addr = __phys_to_virt(end);
         addr < VMALLOC_START; addr += PMD_SIZE)
        pmd_clear(pmd_off_k(addr));
}

 这里调用pmd_clear函数,清除一级页表项的内容
 0~MODULES_VADDR
 MODULES_VADDR~PAGE_OFFSET
 arm_lowmem_limit~VMALLOC_START

真正创建页表的地方:map_lowmem

static void __init map_lowmem(void)
{
    struct memblock_region *reg;
    phys_addr_t kernel_x_start = round_down(__pa(_stext), SECTION_SIZE);
    phys_addr_t kernel_x_end = round_up(__pa(__init_end), SECTION_SIZE);

    /* Map all the lowmem memory banks. */
    for_each_memblock(memory, reg) {
        phys_addr_t start = reg->base;
        phys_addr_t end = start + reg->size;
        struct map_desc map;

        if (end > arm_lowmem_limit)
            end = arm_lowmem_limit;
        if (start >= end)
            break;

        if (end < kernel_x_start) {
            map.pfn = __phys_to_pfn(start);
            map.virtual = __phys_to_virt(start);
            map.length = end - start;
            map.type = MT_MEMORY_RWX;

            create_mapping(&map);
        } else if (start >= kernel_x_end) {
            map.pfn = __phys_to_pfn(start);
            map.virtual = __phys_to_virt(start);
            map.length = end - start;
            map.type = MT_MEMORY_RW;

            create_mapping(&map);
        } else {
            /* This better cover the entire kernel */
            if (start < kernel_x_start) {
                map.pfn = __phys_to_pfn(start);
                map.virtual = __phys_to_virt(start);
                map.length = kernel_x_start - start;
                map.type = MT_MEMORY_RW;

                create_mapping(&map);
            }
            //映射内核image区域
            map.pfn = __phys_to_pfn(kernel_x_start);
            map.virtual = __phys_to_virt(kernel_x_start);
            map.length = kernel_x_end - kernel_x_start;
            map.type = MT_MEMORY_RWX;

            create_mapping(&map);
            //映射低端内存
            if (kernel_x_end < end) {
                map.pfn = __phys_to_pfn(kernel_x_end);
                map.virtual = __phys_to_virt(kernel_x_end);
                map.length = end - kernel_x_end;
                map.type = MT_MEMORY_RW;

                create_mapping(&map);
            }
        }
    }
}