Linux Kernel线性映射代码分析

文章详细描述了LinuxKernel在启动时如何根据内存大小设置线性映射区域,特别是针对ARM64架构的处理,包括设置非执行映射、保护文本和数据区域,以及使用page-level映射进行内存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       在Linux Kernel启动过程中,会根据物理内存的大小以及虚拟内存空间大小,决定线性映射区间的大小。在ARM64架构中,由于虚拟地址空间足够大,正常情况下都会对memory建立线性映射,有的也叫直接映射。

        本文主要是走读线性映射的代码。

static void __init map_mem(pgd_t *pgdp)
{
	static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
	phys_addr_t kernel_start = __pa_symbol(_stext);
	phys_addr_t kernel_end = __pa_symbol(__init_begin);
	phys_addr_t start, end;
	int flags = NO_EXEC_MAPPINGS;
	u64 i;

	/*
	 * Setting hierarchical PXNTable attributes on table entries covering
	 * the linear region is only possible if it is guaranteed that no table
	 * entries at any level are being shared between the linear region and
	 * the vmalloc region. Check whether this is true for the PGD level, in
	 * which case it is guaranteed to be true for all other levels as well.
	 */
	BUILD_BUG_ON(pgd_index(direct_map_end - 1) == pgd_index(direct_map_end));

	if (can_set_direct_map())
		flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;

	/*
	 * Take care not to create a writable alias for the
	 * read-only text and rodata sections of the kernel image.
	 * So temporarily mark them as NOMAP to skip mappings in
	 * the following for-loop
	 */
	memblock_mark_nomap(kernel_start, kernel_end - kernel_start);

#ifdef CONFIG_KEXEC_CORE
	if (crash_mem_map) {
		if (defer_reserve_crashkernel())
			flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
		else if (crashk_res.end)
			memblock_mark_nomap(crashk_res.start,
			    resource_size(&crashk_res));
	}
#endif

	/* map all the memory banks */
	for_each_mem_range(i, &start, &end) {
		if (start >= end)
			break;
		/*
		 * The linear map must allow allocation tags reading/writing
		 * if MTE is present. Otherwise, it has the same attributes as
		 * PAGE_KERNEL.
		 */
		__map_memblock(pgdp, start, end, pgprot_tagged(PAGE_KERNEL),
			       flags);
	}

	/*
	 * Map the linear alias of the [_stext, __init_begin) interval
	 * as non-executable now, and remove the write permission in
	 * mark_linear_text_alias_ro() below (which will be called after
	 * alternative patching has completed). This makes the contents
	 * of the region accessible to subsystems such as hibernate,
	 * but protects it from inadvertent modification or execution.
	 * Note that contiguous mappings cannot be remapped in this way,
	 * so we should avoid them here.
	 */
	__map_memblock(pgdp, kernel_start, kernel_end,
		       PAGE_KERNEL, NO_CONT_MAPPINGS);
	memblock_clear_nomap(kernel_start, kernel_end - kernel_start);

	/*
	 * Use page-level mappings here so that we can shrink the region
	 * in page granularity and put back unused memory to buddy system
	 * through /sys/kernel/kexec_crash_size interface.
	 */
#ifdef CONFIG_KEXEC_CORE
	if (crash_mem_map && !defer_reserve_crashkernel()) {
		if (crashk_res.end) {
			__map_memblock(pgdp, crashk_res.start,
				       crashk_res.end + 1,
				       PAGE_KERNEL,
				       NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS);
			memblock_clear_nomap(crashk_res.start,
					     resource_size(&crashk_res));
		}
	}
#endif
}
  1. 由于rodata_all,DEBUG_PAGEALLOC 和 KFENCE功能需要控制访问PAGE_SIZE粒度的页表,不能映射block页表。
  2. Linux kernel的代码段、core dump kernel的内存区间是read only的,为了方便,需要先设置为no-map。
  3. for_each_mem_range循环的代码块是建立线性映射的核心代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值