static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
			  phys_addr_t paddr, size_t size, size_t pgcount,
			  arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep,
			  gfp_t gfp, size_t *mapped)
{
	arm_lpae_iopte *cptep, pte;
	size_t block_size = ARM_LPAE_BLOCK_SIZE(lvl, data);
	size_t tblsz = ARM_LPAE_GRANULE(data);
	struct io_pgtable_cfg *cfg = &data->iop.cfg;
	int ret = 0, num_entries, max_entries, map_idx_start;

	/* Find our entry at the current level */
	map_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
	ptep += map_idx_start;

	/* If we can install a leaf entry at this level, then do so */
	if (size == block_size) {
		max_entries = ARM_LPAE_PTES_PER_TABLE(data) - map_idx_start;
		num_entries = min_t(int, pgcount, max_entries);
		ret = arm_lpae_init_pte(data, iova, paddr, prot, lvl, num_entries, ptep);
		if (!ret && mapped)
			*mapped += num_entries * size;

		return ret;
	}

	/* We can't allocate tables at the final level */
	if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1))
		return -EINVAL;

	/* Grab a pointer to the next level */
	pte = READ_ONCE(*ptep);
	if (!pte) {
		cptep = __arm_lpae_alloc_pages(tblsz, gfp, cfg);
		if (!cptep)
			return -ENOMEM;

		pte = arm_lpae_install_table(cptep, ptep, 0, cfg);
		if (pte)
			__arm_lpae_free_pages(cptep, tblsz, cfg);
	} else if (!cfg->coherent_walk && !(pte & ARM_LPAE_PTE_SW_SYNC)) {
		__arm_lpae_sync_pte(ptep, 1, cfg);
	}

	if (pte && !iopte_leaf(pte, lvl, data->iop.fmt)) {
		cptep = iopte_deref(pte, data);
	} else if (pte) {
		/* We require an unmap first */
		WARN_ON(!selftest_running);
		return -EEXIST;
	}

	/* Rinse, repeat */
	return __arm_lpae_map(data, iova, paddr, size, pgcount, prot, lvl + 1,
			      cptep, gfp, mapped);
}

1. 首先来看看这些函数的参数都表示什么意思

`__arm_lpae_map` 函数具有以下参数:

1. `struct arm_lpae_io_pgtable *data`:
   - 这是一个指向页表数据结构的指针,其中包含了用于此映射操作的所有上下文和配置信息。

2. `unsigned long iova`:
   - IO 虚拟地址(IOVA)是开始映射的起始地址。它表示要映射的虚拟地址空间中的地址。

3. `phys_addr_t paddr`:
   - 要映射到的物理地址的起始地址。

4. `size_t size`:
   - 要映射的地址范围的大小。

5. `size_t pgcount`:
   - 要映射的页数。

6. `arm_lpae_iopte prot`:
   - 页表项的保护位/权限,例如,它可以指示这是一个只读页面、可写页面等。

7. `int lvl`:
   - 当前正在处理的页表级别。如您所提及的,级别0为最高级,而级别3为最低级。

8. `arm_lpae_iopte *ptep`:
   - 指向当前级别页表的指针,其中应放置映射。

9. `gfp_t gfp`:
   - 内存分配的标志。这些标志描述了内存分配的行为,例如,是否可以休眠,是否应在低内存情况下触发OOM处理等。

10. `size_t *mapped`:
   - 如果提供,此指针将被用来返回实际映射的大小。这可能与请求的大小不同,尤其是在映射操作遇到错误时。

函数的参数主要定义了要映射的地址范围、权限和相关的上下文,以及返回映射结果所需的信息。笔者看到这些参数时候 被两个参数困惑 分别是 要映射的物理地址范围的大小 和 要映射的页数我认为这两个有所重合,询问了GPT 之后得到以下结论:

1. `size_t size`: 这是要映射的连续地址空间的大小。这个大小可能不是页的整数倍。

2. `size_t pgcount`: 这是要映射的页数。在给定`size`的情况下,`pgcount`是这样计算的:`pgcount = (size + PAGE_SIZE - 1) / PAGE_SIZE;`,其中`PAGE_SIZE`是系统页的大小。

在某些情况下,`pgcount`和`size`确实可能表达相同的意思,但它们是从两个不同的角度来看待映射的。`size`告诉我们要映射多少字节,而`pgcount`告诉我们要映射多少页。在某些实现中,可能不需要两者都传递,但这个函数选择了传递两者,可能是为了优化性能或为了代码的清晰性

 

static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
			     unsigned long iova, phys_addr_t paddr,
			     arm_lpae_iopte prot, int lvl, int num_entries,
			     arm_lpae_iopte *ptep)
{
	int i;

	for (i = 0; i < num_entries; i++)
		if (iopte_leaf(ptep[i], lvl, data->iop.fmt)) {
			/* We require an unmap first */
			WARN_ON(!selftest_running);
			return -EEXIST;
		} else if (iopte_type(ptep[i], lvl) == ARM_LPAE_PTE_TYPE_TABLE) {
			/*
			 * We need to unmap and free the old table before
			 * overwriting it with a block entry.
			 */
			arm_lpae_iopte *tblp;
			size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);

			tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data);
			if (__arm_lpae_unmap(data, NULL, iova + i * sz, sz, 1,
					     lvl, tblp) != sz) {
				WARN_ON(1);
				return -EINVAL;
			}
		}

	__arm_lpae_init_pte(data, paddr, prot, lvl, num_entries, ptep);
	return 0;
}

 

posted on 2023-08-07 13:46  赵帅_hiter  阅读(329)  评论(0)    收藏  举报