内存映射:map_memory宏作用分析

对于函数 map_memory 的定义来说,它的作用是:为一段给定的虚拟地址区间 [vstart, vend) 建立页表映射,把它线性映射到一段"连续"的物理内存(phys 起始,对应 vstart),并按需分配/填充中间各级页表

在该函数中,默认页表是按顺序依次排列的,在后续正常启动时会重新分配页表,具体过程大概如此

c 复制代码
/*
 * Map memory for specified virtual address range. Each level of page table needed supports
 * multiple entries. If a level requires n entries the next page table level is assumed to be
 * formed from n pages.
 *
 *	tbl:	location of page table
 *	rtbl:	address to be used for first level page table entry (typically tbl + PAGE_SIZE)
 *	vstart:	virtual address of start of range
 *	vend:	virtual address of end of range - we map [vstart, vend - 1]
 *	flags:	flags to use to map last level entries
 *	phys:	physical address corresponding to vstart - physical memory is contiguous
 *	order:  #imm 2log(number of entries in PGD table)
 *
 * If extra_shift is set, an extra level will be populated if the end address does
 * not fit in 'extra_shift' bits. This assumes vend is in the TTBR0 range.
 *
 * Temporaries:	istart, iend, tmp, count, sv - these need to be different registers
 * Preserves:	vstart, flags
 * Corrupts:	tbl, rtbl, vend, istart, iend, tmp, count, sv
 */
	.macro map_memory, tbl, rtbl, vstart, vend, flags, phys, order, istart, iend, tmp, count, sv, extra_shift
	sub \vend, \vend, #1 // vend - 1
	add \rtbl, \tbl, #PAGE_SIZE // rtbl = tbl + PAGE_SIZE 这里PAGE_SIZE=4KB,一般是PUD基地址
	mov \count, #0 // count = 0

.L_\@:
	// 获取页表项索引个数和具体索引值
	compute_indices \vstart, \vend, #PGDIR_SHIFT, #\order, \istart, \iend, \count
	// sv = rtbl
	mov \sv, \rtbl
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv

#if SWAPPER_PGTABLE_LEVELS > 2
	// 同上,不同的此处的SWAPPER_TABLE_SHIFT是PUD或者PMD shift
	compute_indices \vstart, \vend, #SWAPPER_TABLE_SHIFT, #(PAGE_SHIFT - 3), \istart, \iend, \count
	mov \sv, \rtbl
	populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
	mov \tbl, \sv
#endif

	compute_indices \vstart, \vend, #SWAPPER_BLOCK_SHIFT, #(PAGE_SHIFT - 3), \istart, \iend, \count
	bic \rtbl, \phys, #SWAPPER_BLOCK_SIZE - 1
	populate_entries \tbl, \rtbl, \istart, \iend, \flags, #SWAPPER_BLOCK_SIZE, \tmp
	.endm

compute_indices 的作用是在某一级页表(根据 order/shift )上,把虚拟地址区间 [vstart, vend) 转换为该级页表的起始表项索引 [istart, iend,并通过 count 返回

如对于PGD来说,无上级页表,那么 count 为0,那么所有的页表项索引均在当前页表中

c 复制代码
/*
 * Compute indices of table entries from virtual address range. If multiple entries
 * were needed in the previous page table level then the next page table level is assumed
 * to be composed of multiple pages. (This effectively scales the end index).
 *
 *	vstart:	virtual address of start of range
 *	vend:	virtual address of end of range - we map [vstart, vend]
 *	shift:	shift used to transform virtual address into index
 *	order:  #imm 2log(number of entries in page table)
 *	istart:	index in table corresponding to vstart
 *	iend:	index in table corresponding to vend
 *	count:	On entry: how many extra entries were required in previous level, scales
 *			  our end index.
 *		On exit: returns how many extra entries required for next page table level
 *
 * Preserves:	vstart, vend
 * Returns:	istart, iend, count
 */
	.macro compute_indices, vstart, vend, shift, order, istart, iend, count
	// 页表项索引 istart
	ubfx	\istart, \vstart, \shift, \order
	// 页表项索引 iend,不一定是当前页表
	ubfx	\ , \vend, \shift, \order
	// 根据count,把 iend 变成跨多页的线性下标
	add	\iend, \iend, \count, lsl \order
	// 计算总共需要多少个本级页表索引个数
	sub	\count, \iend, \istart
	.endm

populate_entries 在当前页表中,从 indexeindex 这一段页表依次写入一个条目,大小是一个PMD页表(一般是页PAGE_SIZE)大小,最后返回更新后的 rtbl

c 复制代码
/*
 * Macro to populate page table entries, these entries can be pointers to the next level
 * or last level entries pointing to physical memory.
 *
 *	tbl:	page table address
 *	rtbl:	pointer to page table or physical memory
 *	index:	start index to write
 *	eindex:	end index to write - [index, eindex] written to
 *	flags:	flags for pagetable entry to or in
 *	inc:	increment to rtbl between each entry
 *	tmp1:	temporary variable
 *
 * Preserves:	tbl, eindex, flags, inc
 * Corrupts:	index, tmp1
 * Returns:	rtbl
 */
	.macro populate_entries, tbl, rtbl, index, eindex, flags, inc, tmp1
.Lpe\@:	phys_to_pte \tmp1, \rtbl
	orr	\tmp1, \tmp1, \flags	// tmp1 = table entry
	str	\tmp1, [\tbl, \index, lsl #3]
	add	\rtbl, \rtbl, \inc	// rtbl = pa next level
	add	\index, \index, #1
	cmp	\index, \eindex
	b.ls	.Lpe\@
	.endm
相关推荐
予枫的编程笔记5 天前
【Docker高级篇】吃透容器编排:Swarm vs K8s 核心差异,为后续K8s学习打牢基础
docker·云原生·kubernetes·linux内核·容器编排·容器技术·运维技术
新兴AI民工13 天前
【Linux内核二十】进程管理模块:CFS调度器enqueue(七):enqueue_entity函数的收尾
linux·服务器·linux内核·内核代码学习
皮皮哎哟14 天前
Linux文件IO与目录IO编程深度解析:从系统调用到实战应用
开发语言·linux内核·文件io·目录io·时间相关接口·c预言
bsauce1 个月前
【kernel exploit】CVE-2025-21702-net-sched UAF漏洞分析
linux内核·内核安全·内核漏洞利用
物理与数学1 个月前
linux内核 struct super_block
linux·linux内核
物理与数学1 个月前
Linux 页表映射
linux·linux内核
物理与数学1 个月前
linux内核常用hook机制
linux·linux内核
物理与数学1 个月前
linux内核 页缓存的脏页管理
linux·linux内核
物理与数学1 个月前
Linux 内核 vm_area_struct与vm_struct
linux·linux内核