arch/arm中的页表 相关函数

文章目录

        • 通用知识
        • 其他函数
        • arch 被定义且被非arch索引的pgd p4d pud pmd pte 相关宏及函数
        • 其他函数
          • create_mapping 相关
            • create_mapping
            • create_mapping_late
            • __create_mapping
          • PGD 相关
            • pgd_offset
            • pgd_addr_end
          • P4D 相关
            • alloc_init_p4d
            • p4d_offset
            • p4d_addr_end
          • PUD 相关
            • alloc_init_pud
            • pud_offset
            • pud_addr_end
          • PMD 相关
            • alloc_init_pmd
            • pmd_offset
            • pmd_addr_end
          • PTE 相关
            • alloc_init_pte
            • arm_pte_alloc
            • set_pte_ext

不管arm(arm32在LPAE扩展时支持3级页表,否则最多支持2级页表)提供几级页表,linux是支持5级页表的
在arch/arm处理的时候,5级转换为两级

Linux提供了通用的没有PUD和PMD的相关的配置
	include/asm-generic/pgtable-nopmd.h
	include/asm-generic/pgtable-nopud.h
Linux是一个支持多硬件平台的操作系统,各种硬件芯片的分页并非固定的2级(页全局目录和页表),
	Intel32位处理器而言,就存在3级的情况(页全局目录、页中间目录和页表)
	intel64位系统的时候就成了4级分页。 
所以Linux为了保持良好的兼容性和移植性,系统设计成了以下的5级分页模型,
根据平台环境和配置的情况,通过将页上级目录和页中间目录的索引位设置为0,从而隐藏了页三级目录和页中间目录的存在。
也就是为什么存在PMD_SHIFT、PUD_SHIFT和PGDIR_SHIFT,还有pgtable-nopmd.h、pgtable-nopud.h和Pgtable-2level_types.h

PGD(Page Global Directory )页全局目录
P4D () // PGD和PUD之间增加一个level叫P4D
PUD(Page Upper Directory)页上级目录
PMD(Page Middle Directory)页中级目录
PTE(Page Table Entry) 页表

如果硬件 只有三级页表,则就没有P4D和PUD,这样的话,pgd=p4d=pud
没有p4d的时候,pgd就等于p4d
没有pud的时候,pud等于p4d,则pgd=p4d=pud

arm32 为 两级页表
arm32 模型 为 (pgd=p4d=pud=pmd) (pte)


在ARM32架构中,
可以按段(section)来映射,这是采用单层映射模式。1级页表
使用页面映射需要两层映射结构,页面可以是64KB或4KB大小。2级别表

通用知识

  • 是 asm 还是 asm-generic
asm 优先

https://www.coder.work/article/6722962
#include  		  引用的头文件是 arch/arm/include/asm/gpio.h(如果不存在,则引用include/asm-generic/gpio.h)
#include  引用的是头文件是 include/asm-generic/gpio.h

include/asm-generic/page.h 和 arch/arm/include/asm/page.h 都存在,<asm/page.h> 优先引用 arch/arm/include/asm/page.h
arch/arm/include/asm/page.h 中引用了 arch/arm/include/asm/pgtable-3level-types.h

  • arm32 到底是几级页表

arm32 支持 1(section 1/16MB)2(4/16KB)

// 1 level :pgd -> page 								// MVA中有1个tableindex,1个offset , 只存在一个page table(对应1次加),一个物理page(对应1次加)
// 2 level :pgd -> pte -> page 							// MVA中有2个index,1个offset , , 只存在2个page table(对应2次加),一个物理page(对应1次加)
// 3 level :pgd -> pmd -> pte -> page 					// 3个index,1个offset,只存在3个page table(对应3次加),一个物理page(对应1次加)
// 4 level :pgd -> pud -> pmd -> pte -> page			// 4个index,1个offset,只存在4个page table(对应4次加),一个物理page(对应1次加)
// 5 level :pgd -> p4d -> pud -> pmd -> pte -> page 	// 5个index,1个offset,只存在5个page table(对应5次加),一个物理page(对应1次加)


CONFIG_ARM_LPAE 配置y 则 为 3,否则为两级

arch/arm/include/asm/page.h
146 #ifdef CONFIG_ARM_LPAE                                                           
147 #include <asm/pgtable-3level-types.h>      // 3级                                      
148 #else                                                                            
149 #include <asm/pgtable-2level-types.h>      // 2级                              
150 #endif

arm目前配置为 2(对应asm/pgtable-2level-types.h) 
(pgd=p4d=pud=pmd) (pte)
	两个中间表(页目录表,页表),一个mva地址中存在着两个table index
	
所以应该有 pgtable-nop4d.h, pgtable-nopud.h  pgtable-nopmd.h

如果为2,有  	pgtable-nop4d.h, 	 pgtable-nopud.h , 	   	  	pgtable-nopmd.h
如果为3,(pgtable-nop4d.h, 	 pgtable-nopud.h) ,	没有( 	pgtable-nopmd.h)
如果为4,(pgtable-nop4d.h),没有(pgtable-nopud.h ,   		pgtable-nopmd.h)
如果为5,没有   (pgtable-nop4d.h, 	 pgtable-nopud.h , 	 	  	pgtable-nopmd.h)

  • arm64 页表
ARMv8架构可以支持48位虚拟地址,并配置成4级页表(4K页),或者3级页表(64K页)
有些Linux系统只使用39位虚拟地址(512G内核,512G用户),配置成3级页表(4K页)或者2级页表(64K页)
https://blog.csdn.net/xiao_budong/article/details/81260296
2级页表: mva中有2个table index (类似arm32) //39位虚拟地址,被配置成2级页表(64K页)
3级页表: mva中有3个table index , //48位虚拟地址,并配置成3级页表(64K页)或 39位虚拟地址,被配置成3级页表(4K页)
4级页表: mva中有4个table index , //48位虚拟地址,并配置成4级页表(4K页)
5级页表: armv8 不支持 5级页表,但是linux支持5级页表 // intel 最新 cpu 支持 5级页表

其他函数

  • 其它函数1
  • 其他函数2
  • 页表图示

arch 被定义且被非arch索引的pgd p4d pud pmd pte 相关宏及函数

  • pgd
__pgd // __pgd(addr)本质是一个 u32 的 变量指针,指针值为addr
	typedef struct { unsigned long pgd; } pgd_t;  // u32 pgd;                                               
	include/asm-generic/page.h 中的 #define __pgd(x)    ((pgd_t) { (x) } )
pgd_bad
	include/asm-generic/pgtable-nop4d.h
	static inline int pgd_bad(pgd_t pgd)        { return 0; } 
	// The "pgd_xxx()" functions here are trivial for a folded two-level
	// setup: the p4d is never bad, and a p4d always exists (as it's folded
	// into the pgd entry)
pgd_clear
	include/asm-generic/pgtable-nop4d.h
	static inline void pgd_clear(pgd_t *pgd)    { }
pgd_none
	include/asm-generic/pgtable-nop4d.h
	static inline int pgd_none(pgd_t pgd)       { return 0; }
pgd_present
	include/asm-generic/pgtable-nop4d.h
	static inline int pgd_present(pgd_t pgd)    { return 1; }
pgd_val
	arch/arm/include/asm/pgtable-2level-types.h
	#define pgd_val(x)  ((x).pgd[0])
pgd_free
	arch/arm/mm/pgd.c
	void pgd_free(struct mm_struct *mm, pgd_t *pgd_base);
  • p4d
在 arm32 中 直接 将  pgd 与 p4d  混合了
在 include/asm-generic/pgtable-nop4d.h 中 可以看到
  • pud
__pud // __pud(addr)本质是一个 u32 的 数组首地址,数组有两个成员,两个成员的地址值分别为addr,addr+4
	typedef u32 pmdval_t;
	typedef struct { pmdval_t pgd[2]; } pgd_t;
		// 为什么每个PGD包含了2个相邻的PGD页面目录项
		// 这么设计目的是什么, 为什么就不能设计为 1个(即4个字节)
	typedef struct { pgd_t pgd; } p4d_t;
	typedef struct { p4d_t p4d; } pud_t;
	include/asm-generic/pgtable-nopud.h 中的 #define __pud(x)                ((pud_t) { __p4d(x) })
	#define __p4d(x)                ((p4d_t) { __pgd(x) })
	typedef struct { pgd_t pgd; } p4d_t;
pud_bad
	应该是 include/asm-generic/pgtable-nopmd.h
	static inline int pud_bad(pud_t pud)        { return 0; }
pud_clear
	static inline void pud_clear(pud_t *pud)    { }
pud_page
	#define pud_page(pud)               (pmd_page((pmd_t){ pud }))
	#define pmd_page(pmd)       pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))
pud_populate
	#define pud_populate(mm, pmd, pte)      do { } while (0)
pud_present
	static inline int pud_present(pud_t pud)    { return 1; }
pud_write
	static inline int pud_write(pud_t pud)                                               
	{                                                                                    
	    BUG();                                                                           
	    return 0;                                                                        
	} 
set_pud
	#define set_pud(pudptr, pudval)         set_pmd((pmd_t *)(pudptr), (pmd_t) { pudval })
	
  • pmd

__pmd //__pmd(addr)本质是一个 u32 的 变量指针,指针值为addr
	arch/arm/include/asm/pgtable-2level-types.h 中的 #define __pmd(x)        ((pmd_t) { (x) } )
	typedef struct { pmdval_t pmd; } pmd_t;
	typedef u32 pmdval_t;	
pmd_alloc_one
	arch/arm/include/asm/pgalloc.h
	#define pmd_alloc_one(mm,addr)      ({ BUG(); ((pmd_t *)2); })
pmd_free
	arch/arm/include/asm/pgalloc.h
	#define pmd_free(mm, pmd)       do { } while (0)
pmd_leaf
	include/linux/pgtable.h
	#define pmd_leaf(x) 0
pmd_offset
	include/linux/pgtable.h
	static inline pmd_t *pmd_offset(pud_t *pud, unsigned long address)                   
	{                                                                                    
	    return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(address);                       
	}
pmd_trans_huge
	include/linux/pgtable.h
	static inline int pmd_trans_huge(pmd_t pmd)                                      
	{                                                                                    
	    return 0;                                                                        
	}
pmd_val
	arch/arm/include/asm/pgtable-2level-types.h
	#define pmd_val(x)      ((x).pmd)
pmd_write
	include/linux/pgtable.h
	static inline int pmd_write(pmd_t pmd)                                           
	{                                                                                    
	    BUG();                                                                           
	    return 0;                                                                        
	}
  • pte
__pte // __pgd(value)本质是一个 u32 的 变量,值为value
	arch/arm/include/asm/pgtable-2level-types.h 中的 #define __pte(x)        ((pte_t) { (x) } )
	typedef struct { pteval_t pte; } pte_t;
	typedef u32 pteval_t;
pte_accessible
	arch/arm/include/asm/pgtable.h
	#define pte_accessible(mm, pte) (mm_tlb_flush_pending(mm) ? pte_present(pte) : pte_valid(pte))
pte_access_permitted
	arch/arm/include/asm/pgtable.h
	static inline bool pte_access_permitted(pte_t pte, bool write)                   
	{                                                                                
	    pteval_t mask = L_PTE_PRESENT | L_PTE_USER;                                  
	    pteval_t needed = mask;                                                      
	                                                                                 
	    if (write)                                                                   
	        mask |= L_PTE_RDONLY;                                                    
	                                                                                 
	    return (pte_val(pte) & mask) == needed;                                      
	}
pte_same
	include/linux/pgtable.h
	static inline int pte_same(pte_t pte_a, pte_t pte_b)                                 
	{                                                                                    
	    return pte_val(pte_a) == pte_val(pte_b);                                         
	}
pte_val
	arch/arm/include/asm/pgtable-2level-types.h
	#define pte_val(x)      ((x).pte)
pte_alloc_one
	arch/arm/include/asm/pgalloc.h
	static inline pgtable_t                                                             
	pte_alloc_one(struct mm_struct *mm)                                                 
	{                                                                                   
	    struct page *pte;                                                               
	                                                                                    
	    pte = __pte_alloc_one(mm, GFP_PGTABLE_USER | PGTABLE_HIGHMEM);                  
	    if (!pte)                                                                       
	        return NULL;                                                                
	    if (!PageHighMem(pte))                                                          
	        clean_pte_table(page_address(pte));                                         
	    return pte;                                                                     
	} 
pte_alloc_one_kernel
	arch/arm/include/asm/pgalloc.h
	static inline pte_t *                                                               
	pte_alloc_one_kernel(struct mm_struct *mm)                                          
	{                                                                                   
	    pte_t *pte = __pte_alloc_one_kernel(mm);                                        
	                                                                                    
	    if (pte)                                                                        
	        clean_pte_table(pte);                                                       
	                                                                                    
	    return pte;                                                                     
	}

其他函数

early_pte_alloc
	arm_pte_alloc
		__pmd_populate
		pte_offset_kernel

create_mapping
	__create_mapping
		pgd_offset
		pgd_addr_end
		...
		alloc_init_p4d
			p4d_offset
			p4d_addr_end
			...
			alloc_init_pud
				pud_offset
				pud_addr_end
				...
				alloc_init_pmd
					pmd_offset
					pmd_addr_end
					...
					alloc_init_pte
						arm_pte_alloc
						set_pte_ext


create_mapping_late
	__create_mapping



pmd related
	pmd_off_k
	pmd_off


create_mapping 相关
create_mapping
作用:
	将下面两个区域做mapping,mapping 关系写到 init_mm (swapper_pg_dir)
		起始物理地址等于phys,大小是size的一段物理内存 
		起始虚拟地址等于virt, 大小是size的一段虚拟内存
	映射的memory attribute是prot

	过程中用到的内存申请方法 为 early_alloc

定义:
	arch/arm/mm/mmu.c
声明:
	void __init create_mapping(struct map_desc *md);
参数:
arch/arm/include/asm/mach/map.h
 14 struct map_desc {                                                                
 15     unsigned long virtual;                                                       
 16     unsigned long pfn;                                                           
 17     unsigned long length;                                                        
 18     unsigned int type;                                                           
 19 };
create_mapping_late
作用:
	TODO
声明:
	void __init create_mapping_late(struct mm_struct *mm, struct map_desc *md, bool ng)
参数:
	TODO

__create_mapping
作用:
	将下面两个区域做mapping,mapping 关系写到 参数 (struct mm_struct *mm)
		起始物理地址等于phys,大小是size的一段物理内存 
		起始虚拟地址等于virt, 大小是size的一段虚拟内存
	映射的memory attribute是prot
	
	过程中用到的内存申请方法 为 参数 (alloc)
	
声明:
	static void __init __create_mapping(struct mm_struct *mm, struct map_desc *md, void *(*alloc)(unsigned long sz), bool ng)
参数:
	
PGD 相关
pgd_offset
作用:
	根据 (mm)->pgd 和 虚拟地址 address 获取 一级页表项的地址
	pgd_offset 返回的值 是 根据 一级页表基址(mm_struct中的pgd成员) + address[31:20]>>20
声明:
	pgd_offset(mm, address)
参数:
	struct mm_struct *mm
	unsigned long address
使用方法:
	struct mm_struct *mm;struct map_desc *md;unsigned long addr;pgd_t *pgd;
	addr = md->virtual & PAGE_MASK;
	pgd = pgd_offset(mm, addr); 
	// 这里的 pgd 是某个一级页表项的地址
	// 注意 : mm_struct 中的 pgd 是 一级页表项基址(即第一个一级页表项的地址)
pgd_addr_end

作用:
	根据 addr 和 end 获取 ,下一个 addr
	// 一般返回 addr + 1<<21
	// 一般返回 addr + 2<<21
	// ...
	// 最后一次返回 等于 end
声明:
	include/linux/pgtable.h
	pgd_addr_end(addr, end)
参数:
	addr end
使用方法:
	end = addr + length;
	unsigned long next = pgd_addr_end(addr, end);	
P4D 相关
alloc_init_p4d
根据一级页表项基址,和虚拟地址起始地址addr和结束地址end去创建映射关系
p4d_offset
作用:
	根据 pgd_t *pgd; 和 虚拟地址 addr 获取 一级页表项的地址
	因为在arm32中,pgd=p4d=pud,所以直接返回了 pgd
	// 这里返回的 值被作为 某个二级页表项的地址
声明:
	static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address);
定义:
	include/asm-generic/pgtable-nop4d.h
参数:
	pgd_t *pgd, unsigned long address
p4d_addr_end
作用:
	根据 addr 和 end , 获取 下一个addr
	因为在arm32中,pgd=p4d=pud,相当于p4d没有用到,所以直接返回了 addr
定义:
	include/asm-generic/pgtable-nop4d.h
	#define p4d_addr_end(addr, end)         (end)
PUD 相关
alloc_init_pud
根据二级页表项基址,和虚拟地址起始地址addr和结束地址end去创建映射关系
pud_offset
作用:
	根据 pgd_t *p4d; 和 虚拟地址 addr 获取 一级页表项的地址
	因为在arm32中,pgd=p4d=pud,所以直接返回了 p4d
	// 这里返回的 值被作为 某个三级页表项的地址
	include/asm-generic/pgtable-nopud.h
声明:
	static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address);
定义:
	include/asm-generic/pgtable-nop4d.h
参数:
	p4d_t *p4d, unsigned long address

pud_addr_end
作用:
	根据 addr 和 end , 获取 下一个addr
	因为在arm32中,pgd=p4d=pud,相当于pud没有用到,所以直接返回了 addr
定义:
	include/asm-generic/pgtable-nopud.h
	#define pud_addr_end(addr, end)         (end)
PMD 相关
alloc_init_pmd
根据三级页表项基址,和虚拟地址起始地址addr和结束地址end去创建映射关系
是做一级页表是"段表"还是做"页表"
如果满足 type->prot_sect && ((addr | next | phys) & ~SECTION_MASK) == 0 , 则 做段表
否则,做页表
arm32-linux 实际情况
	map_lowmem // 低端内存相关的映射 
	(map_lowmem 中都是一级SECTION映射,除了map_lowmem,其他都是一级二级页表映射)


pmd_offset
作用:
	根据 pgd_t *pud; 和 虚拟地址 addr 获取 一级页表项的地址
	因为在arm32中,pgd=p4d=pud=pmd,所以直接返回了 p4d
	// 这里返回的 值被作为 某个四级页表项的地址
声明:
	static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
定义:
	arch/arm/include/asm/pgtable-2level.h还是 include/asm-generic/pgtable-nopmd.h
参数:
	pud_t *pud, unsigned long addr
	
pmd_addr_end
作用:
	根据 addr 和 end , 获取 下一个addr
	因为在arm32中,pgd=p4d=pud=pmd,相当于pmd没有用到,所以直接返回了 addr
定义:
	arch/arm/include/asm/pgtable-2level.h 
	#define pmd_addr_end(addr,end) (end)
PTE 相关
alloc_init_pte
根据四级页表项基址,和虚拟地址起始地址addr和结束地址end去创建映射关系
该函数中会 写入页表(填充一级页表项和二级页表项)
arm_pte_alloc
作用:
	根据四级页表项基址(在arm32中和一级页表项基址值相同)和addr 去获取 五级页表项pte的地址
	如果 四级页表项基址 开始的 pmd[0] 和 pmd[1] 为空, 则填充值

返回值:
	// 这里返回的 值被作为 某个五级页表项的地址
set_pte_ext

作用:
	根据 五级页表项 地址 pte 填充二级页表项
	在arm32中, 一次该函数调用 填充 两个连续的二级页表项
声明:
	set_pte_ext(ptep,pte,ext)
参数:
	

arch/arm/include/asm/pgtable-2level.h
	#define set_pte_ext(ptep,pte,ext) cpu_set_pte_ext(ptep,pte,ext)
arch/arm/include/asm/glue-proc.h
	#define cpu_set_pte_ext         __glue(CPU_NAME,_set_pte_ext)
arch/arm/include/asm/glue-proc.h
	#  define CPU_NAME cpu_v6
arch/arm/mm/proc-v6.S
	cpu_v6_set_pte_ext

你可能感兴趣的:(杂七杂八总览,arm,arm开发,嵌入式硬件)