aarch64页表管理[2] 页操作常用宏及位定义简介

页操作常用宏

include/linux/pgtable.h
include

pgd_offset

mm的pgd + pgd_index

static inline pgd_t *pgd_offset_pgd(pgd_t *pgd, unsigned long address)
{return (pgd + pgd_index(address));};

mm->pgd是指针,这个动作含义是指针偏移pgd_index位
pgd_offset = pgdp = mm->pgd[pgd]
(pgd是地址的第40~48位

pud_offset

调用p4d_pgtable + pud_index
p4d_pgtable调用p4d_page_paddr
p4d_page_paddr调用__p4d_to_phys
__p4d_to_phys实际上是 __pte_to_phys宏的变形

static inline pud_t *p4d_pgtable(p4d_t p4d)
{ return (pud_t *)__va(p4d_page_paddr(p4d));}

static inline phys_addr_t p4d_page_paddr(p4d_t p4d)
{ return __p4d_to_phys(p4d);}

#define __p4d_to_phys(p4d)	__pte_to_phys(p4d_pte(p4d))

static inline pte_t p4d_pte(p4d_t p4d)
{ return __pte(p4d_val(p4d));}

#define __pte_to_phys(pte)	(pte_val(pte) & PTE_ADDR_MASK)

static inline pte_t pgd_pte(pgd_t pgd)
{ return __pte(pgd_val(pgd));}
----------
static inline unsigned long pud_index(unsigned long address)
{return (address >> PUD_SHIFT) & (PTRS_PER_PUD - 1);}
p4d_pgtable(p4d_t p4d)
	(pud_t *)__va(p4d_page_paddr(p4d))
p4d_page_paddr(p4d_t p4d)
	__p4d_to_phys(p4d_t p4d)
		__pte_to_phys(p4d_pte(p4d))
			(pte_val(p4d_pte(p4d)) & PTE_ADDR_MASK)
				(pte_val(__pte(p4d_val(p4d)))) & PTE_ADDR_MASK)

解释:对一个p4d_t获取p4dval_t,再强转成pte_t,再获取pteval_t,再&上PTE_ADDR_MASK (12-48位),再用__va宏取虚拟地址,再转成pud_t类型返回
概况:对传入的p4d(pgdp指向的值),&上0xffff-ffff-f000(13-48位),然后把得到的值用va宏获取地址

(为什么返回pud_t而底层调用的是pte类型?我感觉所因为操作方法,pud pmd pte类型其实都一样都是u64,直接使用通用的pte类型了)

通过全局pgd偏移pgd位的方式得到pgdp
pgdp指向的是,目标地址所在的pud的地址,(二级页目录入口 pud-entry
pud-entry = pgdp = mm->pgd[pgd]
pmd-entry = pudp = pud-entry[pud]
pte-entry = pmdp = pmd-entry[pmd]
ptep = pte-entry[pte]
addr = ptep[page] (低12位

pmd_offset

(流程和pud_offset一样)
调用pud_pgtable + pmd_index
pud_pgtable调用pud_page_paddr
pud_page_paddr调用__pud_to_phys
__pud_to_phys实际上是 __pte_to_phys宏的变形

static inline pmd_t *pud_pgtable(pud_t pud)
{return (pmd_t *)__va(pud_page_paddr(pud));}

static inline phys_addr_t pud_page_paddr(pud_t pud)
{return __pud_to_phys(pud);}

#define __pud_to_phys(pud)	__pte_to_phys(pud_pte(pud))

static inline pte_t pud_pte(pud_t pud)
{return __pte(pud_val(pud));}
----------
static inline unsigned long pmd_index(unsigned long address)
{return (address >> PMD_SHIFT) & (PTRS_PER_PMD - 1);}

pte_offset_kernel

(流程和pmd_offset有些许差别)
调用pmd_page_vaddr + pte_index (而非pmd_pgtable)
pmd_page_vaddr调用pmd_page_paddr (va->pa)
pmd_page_paddr调用__pmd_to_phys
__pmd_to_phys实际上是 __pte_to_phys宏的变形

static inline pte_t *pte_offset_kernel(pmd_t *pmd, unsigned long address)
{return (pte_t *)pmd_page_vaddr(*pmd) + pte_index(address);}

static inline unsigned long pmd_page_vaddr(pmd_t pmd)
{return (unsigned long)__va(pmd_page_paddr(pmd));}

static inline phys_addr_t pmd_page_paddr(pmd_t pmd)
{return __pmd_to_phys(pmd);}

#define __pmd_to_phys(pmd)	__pte_to_phys(pmd_pte(pmd))

pte_valid

#define pte_valid(pte)		(!!(pte_val(pte) & PTE_VALID))
// PTE_VALID宏见下一节的bit位

这个宏是用pte的值和1相与

set_pte_bit 和 clear_pte_bit

arm64 pgtable-types.h

typedef struct { pteval_t pte; } pte_t;
#define pte_val(x)	((x).pte)
#define __pte(x)	((pte_t) { (x) } )

typedef struct { pteval_t pgprot; } pgprot_t;
#define pgprot_val(x)	((x).pgprot)
#define __pgprot(x)	((pgprot_t) { (x) } )

pgprot_val是取一个pteval_t类型的的值(类型转换而已,实际上就是一个u64类型的pte)
pgprot_t看样子像是pgd pud pmd pte之类的通用类型

static inline pte_t clear_pte_bit(pte_t pte, pgprot_t prot)
{
	pte_val(pte) &= ~pgprot_val(prot);
	return pte;
}

static inline pte_t set_pte_bit(pte_t pte, pgprot_t prot)
{
	pte_val(pte) |= pgprot_val(prot);
	return pte;
}

READ_ONCE 和 WRITE_ONCE

READ_ONCE,arm64实现这个有点复杂,可以先看下通用的理解下

#define READ_ONCE(x)							\
({									\
	compiletime_assert_rwonce_type(x);				\
	__READ_ONCE(x);							\
})

// include/asm-genernic/rwonce.h
#ifndef __READ_ONCE
#define __READ_ONCE(x)	(*(const volatile __unqual_scalar_typeof(x) *)&(x))
#endif
// arch/arm64/include/asm/rwonce.h
#define __READ_ONCE(x)							\
({									\
	typeof(&(x)) __x = &(x);					\
	int atomic = 1;							\
	union { __unqual_scalar_typeof(*__x) __val; char __c[1]; } __u;	\
	switch (sizeof(x)) {						\
	case 1:								\
		asm volatile(__LOAD_RCPC(b, %w0, %1)			\
			: "=r" (*(__u8 *)__u.__c)			\
			: "Q" (*__x) : "memory");			\
		break;							\
	case 2:								\
		asm volatile(__LOAD_RCPC(h, %w0, %1)			\
			: "=r" (*(__u16 *)__u.__c)			\
			: "Q" (*__x) : "memory");			\
		break;							\
	case 4:								\
		asm volatile(__LOAD_RCPC(, %w0, %1)			\
			: "=r" (*(__u32 *)__u.__c)			\
			: "Q" (*__x) : "memory");			\
		break;							\
	case 8:								\
		asm volatile(__LOAD_RCPC(, %0, %1)			\
			: "=r" (*(__u64 *)__u.__c)			\
			: "Q" (*__x) : "memory");			\
		break;							\
	default:							\
		atomic = 0;						\
	}								\
	atomic ? (typeof(*__x))__u.__val : (*(volatile typeof(__x))__x);\
})
#define WRITE_ONCE(x, val)						\
do {									\
	compiletime_assert_rwonce_type(x);				\
	__WRITE_ONCE(x, val);						\
} while (0)

#define __WRITE_ONCE(x, val)						\
do {									\
	*(volatile typeof(x) *)&(x) = (val);				\
} while (0)

// e.g
int x = 1;
*(volatile int *) &(x) = 2;
// x被赋值为2
// val先取地址,再用(int*)修饰成int* 类型的指针,再用*解引用,然后赋值
// 所以WRITE_ONCE就是对变量x赋值,而已

页表相关标志位

pte的位

arch/arm64/include/asm/pgtable-prot.h

#define PTE_WRITE		(PTE_DBM)		 /* same as DBM (51) */
#define PTE_SWP_EXCLUSIVE	(_AT(pteval_t, 1) << 2)	 /* only for swp ptes */
#define PTE_DIRTY		(_AT(pteval_t, 1) << 55)
#define PTE_SPECIAL		(_AT(pteval_t, 1) << 56)
#define PTE_DEVMAP		(_AT(pteval_t, 1) << 57)
#define PTE_PROT_NONE		(_AT(pteval_t, 1) << 58) /* only when !PTE_VALID */

arch/arm64/include/asm/pgtable-hwdef.h

#define PTE_VALID		(_AT(pteval_t, 1) << 0)
#define PTE_TYPE_MASK		(_AT(pteval_t, 3) << 0)
#define PTE_TYPE_PAGE		(_AT(pteval_t, 3) << 0)
#define PTE_TABLE_BIT		(_AT(pteval_t, 1) << 1)
#define PTE_USER		(_AT(pteval_t, 1) << 6)		/* AP[1] */
#define PTE_RDONLY		(_AT(pteval_t, 1) << 7)		/* AP[2] */
#define PTE_SHARED		(_AT(pteval_t, 3) << 8)		/* SH[1:0], inner shareable */
#define PTE_AF			(_AT(pteval_t, 1) << 10)	/* Access Flag */
#define PTE_NG			(_AT(pteval_t, 1) << 11)	/* nG */
#define PTE_GP			(_AT(pteval_t, 1) << 50)	/* BTI guarded */
#define PTE_DBM			(_AT(pteval_t, 1) << 51)	/* Dirty Bit Management */
#define PTE_CONT		(_AT(pteval_t, 1) << 52)	/* Contiguous range */
#define PTE_PXN			(_AT(pteval_t, 1) << 53)	/* Privileged XN */
#define PTE_UXN			(_AT(pteval_t, 1) << 54)	/* User XN */

arch/arm64/include/asm/pgtable.h

#define pte_present(pte)	(!!(pte_val(pte) & (PTE_VALID | PTE_PROT_NONE)))
#define pte_young(pte)		(!!(pte_val(pte) & PTE_AF))
#define pte_special(pte)	(!!(pte_val(pte) & PTE_SPECIAL))
#define pte_write(pte)		(!!(pte_val(pte) & PTE_WRITE))
#define pte_rdonly(pte)		(!!(pte_val(pte) & PTE_RDONLY))
#define pte_user(pte)		(!!(pte_val(pte) & PTE_USER))
#define pte_user_exec(pte)	(!(pte_val(pte) & PTE_UXN))
#define pte_cont(pte)		(!!(pte_val(pte) & PTE_CONT))
#define pte_devmap(pte)		(!!(pte_val(pte) & PTE_DEVMAP))
#define pte_tagged(pte)		((pte_val(pte) & PTE_ATTRINDX_MASK) == PTE_ATTRINDX(MT_NORMAL_TAGGED))

static inline pte_t pte_mkwrite_novma(pte_t pte)
{
	pte = set_pte_bit(pte, __pgprot(PTE_WRITE));
	pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY));
	return pte;
}

设置pte可写,可以调用pte_mkwrite_novma,也可以直接调用的set/clear_pte_bit

pmd的位

#define pmd_dirty(pmd)		pte_dirty(pmd_pte(pmd))
#define pmd_young(pmd)		pte_young(pmd_pte(pmd))
#define pmd_valid(pmd)		pte_valid(pmd_pte(pmd))
#define pmd_user(pmd)		pte_user(pmd_pte(pmd))
#define pmd_user_exec(pmd)	pte_user_exec(pmd_pte(pmd))
#define pmd_cont(pmd)		pte_cont(pmd_pte(pmd))
#define pmd_wrprotect(pmd)	pte_pmd(pte_wrprotect(pmd_pte(pmd)))
#define pmd_mkold(pmd)		pte_pmd(pte_mkold(pmd_pte(pmd)))
#define pmd_mkwrite_novma(pmd)	pte_pmd(pte_mkwrite_novma(pmd_pte(pmd)))
#define pmd_mkclean(pmd)	pte_pmd(pte_mkclean(pmd_pte(pmd)))
#define pmd_mkdirty(pmd)	pte_pmd(pte_mkdirty(pmd_pte(pmd)))
#define pmd_mkyoung(pmd)	pte_pmd(pte_mkyoung(pmd_pte(pmd)))

观察这些宏可见,pmd的相关动作都是把pmd转成pte,然后操作pte的bit位
比如pmd_mkwrite_novma,就是把pmd类型转换成pte之后直接调用pte_mkwrite_novma

不难看出,pmd和pte的bit位功能实际上一致

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值