memblock-1
Ref:
http://www.wowotech.net/memory_management/memory-layout.html
memblock将内存块分成两种类型:一种是memory type,另外一种是reserved type,分别用数组来管理系统中的两种类型的memory region。
memblock数组的初始化是根据dts的解析来的,主要解析其chosen、memory以及reserved node。
memblock中的那些数组定义的地址都是物理地址。
在map_mem()函数中,为所有memory type类型的数组创建地址映射的时候,有no-map属性的那段内存地址将不会创建地址映射,也就不在OS的控制范围内了。
memory type region的构建
1、扫描device tree
在完成fdt内存区域的地址映射之后(fixmap_remap_fdt),内核会对fdt进行扫描,以便完成memory type数组的构建。具体代码位于setup_machine_fdt--->early_init_dt_scan--->early_init_dt_scan_nodes中:
void __init early_init_dt_scan_nodes(void)
{
int rc = 0;
/* Retrieve various information from the /chosen node */
rc = of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
if (!rc)
pr_warn("No chosen node found, continuing without\n");
/* Initialize {size,address}-cells info */
of_scan_flat_dt(early_init_dt_scan_root, NULL);
/* Setup memory, calling early_init_dt_add_memory_arch */
of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}
传统的命令行参数解析
early_init_dt_scan_chosen()
memory node解析
early_init_dt_scan_memory():
- 解析memory节点,获取内存的基础布局。
- memory node的reg属性值其实就是一个数组,数组中的每一个entry都是base address和size的二元组。
- 最后,通过early_init_dt_add_memory_arch()将每段内存区域加入memory type region。
2、解析early option: mem
setup_arch--->parse_early_param函数中会对early options解析。我们这里主要关注“mem”option。
/*
* Limit the memory size that was specified via FDT.
*/
static int __init early_mem(char *p)
{
if (!p)
return 1;
memory_limit = memparse(p, &p) & PAGE_MASK;
pr_notice("Memory limited to %lldMB\n", memory_limit >> 20);
return 0;
}
early_param("mem", early_mem);
在ARM64中,由于强制使用device tree,因此mem这个启动参数失去了本来的意义,现在它只是定义了memory的上限(最大的系统内存地址),可以限制DTS传递过来的内存参数。
reserved type region的构建
保留内存的定义主要在fixmap_remap_fdt和arm64_memblock_init函数中进行。
1、保留fdt占用的内存
static void __init setup_machine_fdt(phys_addr_t dt_phys)
{
void *dt_virt = fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL);
if (dt_virt)
memblock_reserve(dt_phys, size);
/* Early fixups are done, map the FDT as read-only now */
fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
}
2、保留内核和initrd占用的内容
void __init arm64_memblock_init(void)
{
/*
* Register the kernel text, kernel data, initrd, and initial
* pagetables with memblock.
*/
memblock_reserve(__pa_symbol(_text), _end - _text);
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size) {
/* the generic initrd code expects virtual addresses */
initrd_start = __phys_to_virt(phys_initrd_start);
initrd_end = initrd_start + phys_initrd_size;
}
}
3、通过early_init_fdt_scan_reserved_mem函数来保留内存
- 分析fdt中的 /memreserve/ fields ,保留每一个/memreserve/ fields定义的memory region。
- __fdt_scan_reserved_mem函数,进行reserved-memory节点的扫描,之后调用fdt_init_reserved_mem函数进行内存预留
4、通过命令行参数保留CMA内存
arm64_memblock_init--->dma_contiguous_reserve函数中会根据命令行参数进行CMA内存的保留