#define ARM_LPAE_GRANULE(d)						\
	(sizeof(arm_lpae_iopte) << (d)->bits_per_level)   /*计算出一个page的大小(byte),也就是io页表的颗粒度*/
#define ARM_LPAE_PGD_SIZE(d)						\
	(sizeof(arm_lpae_iopte) << (d)->pgd_bits)        /*计算出PGD 的大小(byte) */

#define ARM_LPAE_PTES_PER_TABLE(d)					\
	(ARM_LPAE_GRANULE(d) >> ilog2(sizeof(arm_lpae_iopte))) /* 计算了在一个指定粒度的页表中有多少个页表项 (PTEs)*/

 

#define ARM_LPAE_LVL_SHIFT(l,d)						\
	(((ARM_LPAE_MAX_LEVELS - (l)) * (d)->bits_per_level) +		\
	ilog2(sizeof(arm_lpae_iopte)))

计算出要得到level l 的索引 需要向右移动多少个bits,在这个宏的基础上可以得到下面这个宏

#define ARM_LPAE_BLOCK_SIZE(l,d)    (1ULL << ARM_LPAE_LVL_SHIFT(l,d))计算出level 可以映射出的size

#define ARM_LPAE_PGD_IDX(l,d)						\
	((l) == (d)->start_level ? (d)->pgd_bits - (d)->bits_per_level : 0)

#define ARM_LPAE_LVL_IDX(a,l,d)						\
	(((u64)(a) >> ARM_LPAE_LVL_SHIFT(l,d)) &			\
	 ((1 << ((d)->bits_per_level + ARM_LPAE_PGD_IDX(l,d))) - 1))

计算出level l 在io_pagetable中 的索引。

#define iopte_type(pte,l)					\
	(((pte) >> ARM_LPAE_PTE_TYPE_SHIFT) & ARM_LPAE_PTE_TYPE_MASK)

判断出给出的页表项表示的是一个block page 还是一个table,arm的 页表项指向的内容由bit[1:0]来决定 ,0b11 代表page 或者table 0b01 代表block 0bx0 表示invalid,具体可以参考ARMV8 架构参考手册 Figure D5-15.

#define iopte_prot(pte)	((pte) & ARM_LPAE_PTE_ATTR_MASK)

从给出的页表条目中得到相应的属性,如下图所示

static inline bool iopte_leaf(arm_lpae_iopte pte, int lvl,
			      enum io_pgtable_fmt fmt)
{
	if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE)
		return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_PAGE;

	return iopte_type(pte, lvl) == ARM_LPAE_PTE_TYPE_BLOCK;
}

这个函数iopte_leaf是一个内联函数,用于判断给定的页表条目(pte)是否是一个叶子条目。在页表的上下文中,叶子条目通常是指向实际物理内存的条目,而不是指向另一个页表的条目。

  • if (lvl == (ARM_LPAE_MAX_LEVELS - 1) && fmt != ARM_MALI_LPAE): 这个条件检查是否当前级别是最后一个级别,并且格式不是ARM_MALI_LPAE。如果是,那么它会检查pte的类型是否是ARM_LPAE_PTE_TYPE_PAGE,这意味着它是一个指向实际页面的叶子条目。
  • 如果上述条件不满足,那么函数会检查pte的类型是否是ARM_LPAE_PTE_TYPE_BLOCK。在某些级别,块条目可以被视为叶子条目,因为它们映射了一个连续的物理内存块,而不是指向另一个页表
static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte,
				  struct arm_lpae_io_pgtable *data)
{
	u64 paddr = pte & ARM_LPAE_PTE_ADDR_MASK;

	if (ARM_LPAE_GRANULE(data) < SZ_64K)
		return paddr;

	/* Rotate the packed high-order bits back to the top */
	return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);
}

这个函数iopte_to_paddr的目的是从给定的页表条目(pte)中提取物理地址。在ARM的LPAE页表系统中,页表条目不仅仅包含物理地址,还包含其他的元数据,如权限位、属性位等。因此,需要一个函数来从页表条目中提取实际的物理地址。

  1. 函数参数:

    • pte: 要从中提取物理地址的页表条目。
    • data: 指向LPAE页表数据的指针,它包含有关页表的配置和状态的信息。
  2. 提取物理地址:

    • u64 paddr = pte & ARM_LPAE_PTE_ADDR_MASK;: 这一行使用一个掩码ARM_LPAE_PTE_ADDR_MASK来从pte中提取物理地址。这个掩码确保只有物理地址部分被提取,而其他的元数据位被忽略。
  3. 处理小于64K的粒度:

    • if (ARM_LPAE_GRANULE(data) < SZ_64K) return paddr;: 如果当前的页表粒度小于64K,那么直接返回提取的物理地址。
  4. 处理64K或更大的粒度:

    • 对于64K或更大的粒度,物理地址的高位可能被打包到地址的低位。因此,需要进行一些操作来将这些高位旋转回到它们应该在的位置。
    • return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);: 这一行首先将paddr左移,将高位旋转到它们应该在的位置。然后,它使用一个修改过的掩码来确保只有正确的位被包括在最终的物理地址中。
posted on 2023-08-06 15:56  赵帅_hiter  阅读(224)  评论(0)    收藏  举报