[Linux Kernel Memory Management] memblock allocator ⑤ (The location of defining the memblock allocator in the Linux kernel | Linux kernel initialization memblock allocator process under the ARM64 architecture)





1. The location where the memblock allocator is defined in the Linux kernel



The Linux kernel defines the location of the memblock allocator:

The Linux kernel source code linux-4.12\mm\memblock.c #34 position, defines the variable of struct memblocktype , when the structure is assigned, set.bottom_up = false to false, indicating that the memory is allocated from the high address downward;bottom_up

struct memblock memblock __initdata_memblock = {
    
    
	.memory.regions		= memblock_memory_init_regions,
	.memory.cnt		= 1,	/* empty dummy entry */
	.memory.max		= INIT_MEMBLOCK_REGIONS,
	.memory.name		= "memory",

	.reserved.regions	= memblock_reserved_init_regions,
	.reserved.cnt		= 1,	/* empty dummy entry */
	.reserved.max		= INIT_MEMBLOCK_REGIONS,
	.reserved.name		= "reserved",

#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP
	.physmem.regions	= memblock_physmem_init_regions,
	.physmem.cnt		= 1,	/* empty dummy entry */
	.physmem.max		= INIT_PHYSMEM_REGIONS,
	.physmem.name		= "physmem",
#endif

	.bottom_up		= false,
	.current_limit		= MEMBLOCK_ALLOC_ANYWHERE,
};

Source code path: linux-4.12\mm\memblock.c #34

insert image description here





2. Linux kernel initialization memblock allocator process under ARM64 architecture



First call the function in the kernel linux-4.12\init #488 ,asmlinkage __visible void __init start_kernel(void)setup_arch(&command_line);

insert image description here

Then call the source code corresponding to the ARM64 architecture , the function at the location of linux-4.12\arch\arm64\mm\init.c #362 , which is the core function to initialize the memblock allocator;void __init arm64_memblock_init(void)

insert image description here


Linux kernel initialization memblock allocator process under ARM64 architecture:

① Parse the "device tree binary file" /memorynode, and bring "all physical memory" under the management of the memblock allocator;

The Linux source code corresponding to this step is located in linux-4.12\arch\arm64\mm\init.c #367 ;

	/* Handle linux,usable-memory-range property */
	fdt_enforce_memory_region();

Source path: linux-4.12\arch\arm64\mm\init.c #367
insert image description here

② Set global variables memstart_addr to record the starting address of physical memory;

	/*
	 * Select a suitable value for the base of physical memory.
	 */
	memstart_addr = round_down(memblock_start_of_DRAM(),
				   ARM64_MEMSTART_ALIGN);

Source code path: linux-4.12\arch\arm64\mm\init.c #379
insert image description here

③ From the memblock allocator, delete "the specified range of physical memory that cannot be covered by the linear mapping area";

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

Source path: linux-4.12\arch\arm64\mm\init.c #387
insert image description here

④ Use mem to specify the "available memory" size of the "specified command line in the device tree binary file",

If the available memory size is specified, the part of the physical memory beyond the size needs to be deleted from the memblock allocator;

	/*
	 * 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_t)ULLONG_MAX) {
    
    
		memblock_mem_limit_remove_map(memory_limit);
		memblock_add(__pa_symbol(_text), (u64)(_end - _text));
	}

Source code path: linux-4.12\arch\arm64\mm\init.c #401
insert image description here
⑤ Bring the physical memory occupied by the kernel image under the management of the memblock allocator;

	/*
	 * Register the kernel text, kernel data, initrd, and initial
	 * pagetables with memblock.
	 */
	memblock_reserve(__pa_symbol(_text), _end - _text);

Source code path: linux-4.12\arch\arm64\mm\init.c #456
insert image description here

⑥ Initialize the relevant fields and nodes of the device source file corresponding to the "device tree binary file" memory area;

	early_init_fdt_scan_reserved_mem();

Source code path: linux-4.12\arch\arm64\mm\init.c #467

insert image description here





3. Complete source code of arm64_memblock_init function



void __init arm64_memblock_init(void)
{
    
    
	const s64 linear_region_size = -(s64)PAGE_OFFSET;

	/* Handle linux,usable-memory-range property */
	fdt_enforce_memory_region();

	/*
	 * Ensure that the linear region takes up exactly half of the kernel
	 * virtual address space. This way, we can distinguish a linear address
	 * from a kernel/module/vmalloc address by testing a single bit.
	 */
	BUILD_BUG_ON(linear_region_size != BIT(VA_BITS - 1));

	/*
	 * Select a suitable value for the base of physical memory.
	 */
	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);
	}

	/*
	 * 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_t)ULLONG_MAX) {
    
    
		memblock_mem_limit_remove_map(memory_limit);
		memblock_add(__pa_symbol(_text), (u64)(_end - _text));
	}

	if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && initrd_start) {
    
    
		/*
		 * 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 = initrd_start & PAGE_MASK;
		u64 size = PAGE_ALIGN(initrd_end) - 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")) {
    
    
			initrd_start = 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 = range / ARM64_MEMSTART_ALIGN + 1;
			memstart_addr -= ARM64_MEMSTART_ALIGN *
					 ((range * memstart_offset_seed) >> 16);
		}
	}

	/*
	 * Register the kernel text, kernel data, initrd, and initial
	 * pagetables with memblock.
	 */
	memblock_reserve(__pa_symbol(_text), _end - _text);
#ifdef CONFIG_BLK_DEV_INITRD
	if (initrd_start) {
    
    
		memblock_reserve(initrd_start, initrd_end - initrd_start);

		/* the generic initrd code expects virtual addresses */
		initrd_start = __phys_to_virt(initrd_start);
		initrd_end = __phys_to_virt(initrd_end);
	}
#endif

	early_init_fdt_scan_reserved_mem();

	/* 4GB maximum for 32-bit only capable devices */
	if (IS_ENABLED(CONFIG_ZONE_DMA))
		arm64_dma_phys_limit = max_zone_dma_phys();
	else
		arm64_dma_phys_limit = PHYS_MASK + 1;

	reserve_crashkernel();

	reserve_elfcorehdr();

	dma_contiguous_reserve(arm64_dma_phys_limit);

	memblock_allow_resize();
}

Guess you like

Origin blog.csdn.net/han1202012/article/details/124280479