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;
}
浙公网安备 33010602011771号