Linux内存从0到1学习笔记(六,物理内存初始化) --- 更新中
Posted 高桐@BILL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内存从0到1学习笔记(六,物理内存初始化) --- 更新中相关的知识,希望对你有一定的参考价值。
写在前面
kernel启动阶段的物理内存初始化流程如下:
- 获取线性地址大小,记录物理内存的起始地址,以及通过memblock分配器来初始化系统预留内存。
- 初始化内存基本数据结构包括内存节点,内存域;
- 初始化系统分页机制;
- 初始化zonelist;
- 初始化内存分配器;
一、物理内存数据结构
二、物理内存初始化流程
2.1 获取线性地址大小,记录物理内存的起始地址,以及通过memblock分配器来初始化系统预留内存
Linux内核使用伙伴系统管理内存,那么在伙伴系统之前,内核使通过memblock来管理。在系统启动阶段,使用memblock记录物理内存的使用情况。memblock就是将以上内存按功能划分为若干内存区,使用不同的类型存放在memory和reserved的两个集合中,memory即为动态内存,而resvered包括静态内存等。对于系统预留内存,包括静态内存(内核image,ramdisk,fdt等占用空间),以及camera,display等作系统预留的大量连续内存。这部分都是永久分配出去,不会再被伙伴系统管理。
这个函数的主要作用是初始化内存块。获取memory的区间范围,,remove一些超出实际物理内存地址空间的区域,并将kernel text, kernel data, initrd,initial agetables,crash kernel,elf core header到预留内存中。kernel 5.x的为CMA预留连续的物理内存初始化被移到了bootmem_init。
root/arch/arm64/mm/init.c
void __init arm64_memblock_init(void)
//获取线性地址的大小
const s64 linear_region_size = BIT(vabits_actual - 1);
//处理linux可用内存范围的属性
fdt_enforce_memory_region();
//从memblock.memory上移除超出实际物理地址的地址空间;
memblock_remove(1ULL << PHYS_MASK_SHIFT, ULLONG_MAX);
//选择一个合适的值作为内核加载地址对应的物理地址值,保存在memstart_addr;也就是说memstart_addr就是物理内存的起始地址;
memstart_addr = round_down(memblock_start_of_DRAM(),
ARM64_MEMSTART_ALIGN);
/*
* Remove the memory that we will not be able to cover with the
* linear mapping. Take care not to clip the kernel which may be
* high in memory.
*/
memblock_remove(max_t(u64, memstart_addr + linear_region_size,
__pa_symbol(_end)), ULLONG_MAX);
if (memstart_addr + linear_region_size < memblock_end_of_DRAM())
/* ensure that memstart_addr remains sufficiently aligned */
memstart_addr = round_up(memblock_end_of_DRAM() - linear_region_size,
ARM64_MEMSTART_ALIGN);
memblock_remove(0, memstart_addr);
/*
* If we are running with a 52-bit kernel VA config on a system that
* does not support it, we have to place the available physical
* memory in the 48-bit addressable part of the linear region, i.e.,
* we have to move it upward. Since memstart_addr represents the
* physical address of PAGE_OFFSET, we have to *subtract* from it.
*/
if (IS_ENABLED(CONFIG_ARM64_VA_BITS_52) && (vabits_actual != 52))
memstart_addr -= _PAGE_OFFSET(48) - _PAGE_OFFSET(52);
/*
* Apply the memory limit if it was set. Since the kernel may be loaded
* high up in memory, add back the kernel region that must be accessible
* via the linear mapping.
*/
if (memory_limit != PHYS_ADDR_MAX)
memblock_mem_limit_remove_map(memory_limit);
memblock_add(__pa_symbol(_text), (u64)(_end - _text));
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size)
/*
* Add back the memory we just removed if it results in the
* initrd to become inaccessible via the linear mapping.
* Otherwise, this is a no-op
*/
u64 base = phys_initrd_start & PAGE_MASK;
u64 size = PAGE_ALIGN(phys_initrd_start + phys_initrd_size) - base;
/*
* We can only add back the initrd memory if we don't end up
* with more memory than we can address via the linear mapping.
* It is up to the bootloader to position the kernel and the
* initrd reasonably close to each other (i.e., within 32 GB of
* each other) so that all granule/#levels combinations can
* always access both.
*/
if (WARN(base < memblock_start_of_DRAM() ||
base + size > memblock_start_of_DRAM() +
linear_region_size,
"initrd not fully accessible via the linear mapping -- please check your bootloader ...\\n"))
phys_initrd_size = 0;
else
memblock_remove(base, size); /* clear MEMBLOCK_ flags */
memblock_add(base, size);
memblock_reserve(base, size);
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE))
extern u16 memstart_offset_seed;
u64 range = linear_region_size -
(memblock_end_of_DRAM() - memblock_start_of_DRAM());
/*
* If the size of the linear region exceeds, by a sufficient
* margin, the size of the region that the available physical
* memory spans, randomize the linear region as well.
*/
if (memstart_offset_seed > 0 && range >= ARM64_MEMSTART_ALIGN)
range /= ARM64_MEMSTART_ALIGN;
memstart_addr -= ARM64_MEMSTART_ALIGN *
((range * memstart_offset_seed) >> 16);
//将一块物理内存区块加入到预留物理内存内,通过memblock注册kernel text, kernel data, initrd和initial agetables到预留内存中。
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;
//为crash kernel预留内存
early_init_fdt_scan_reserved_mem();
//为elf core header预留内存
reserve_elfcorehdr();
high_memory = __va(memblock_end_of_DRAM() - 1) + 1;
- 初始化内存基本数据结构包括内存节点,内存域;
- 初始化系统分页机制;
- 初始化zonelist;
- 初始化内存分配器;
以上是关于Linux内存从0到1学习笔记(六,物理内存初始化) --- 更新中的主要内容,如果未能解决你的问题,请参考以下文章
Linux内存从0到1学习笔记(六,物理内存初始化之三 --- 物理内存管理数据结构)
Linux内存从0到1学习笔记(六,物理内存初始化之二 --- 内存模型)
Linux内存从0到1学习笔记(6.4,物理内存初始化之预留内存)
Linux内存从0到1学习笔记(6.8,物理内存初始化之buddy伙伴系统)