文章目录
-
-
-
- 通用知识
- 其他函数
- arch 被定义且被非arch索引的pgd p4d pud pmd pte 相关宏及函数
- 其他函数
-
- create_mapping 相关
-
- create_mapping
- create_mapping_late
- __create_mapping
- PGD 相关
-
- 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 ()
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 优先
https:
#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 支持 1级(section 1/16MB) 和 2级(4/16KB)
CONFIG_ARM_LPAE 配置y 则 为 3级,否则为两级
arch/arm/include/asm/page.h
146 #ifdef CONFIG_ARM_LPAE
147 #include <asm/pgtable-3level-types.h>
148 #else
149 #include <asm/pgtable-2level-types.h>
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)
ARMv8架构可以支持48位虚拟地址,并配置成4级页表(4K页),或者3级页表(64K页)
有些Linux系统只使用39位虚拟地址(512G内核,512G用户),配置成3级页表(4K页)或者2级页表(64K页)
https:
2级页表: mva中有2个table index (类似arm32)
3级页表: mva中有3个table index ,
4级页表: mva中有4个table index ,
5级页表: armv8 不支持 5级页表,但是linux支持5级页表
其他函数
arch 被定义且被非arch索引的pgd p4d pud pmd pte 相关宏及函数
__pgd
typedef struct { unsigned long pgd; } pgd_t;
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; }
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);
在 arm32 中 直接 将 pgd 与 p4d 混合了
在 include/asm-generic/pgtable-nop4d.h 中 可以看到
__pud
typedef u32 pmdval_t;
typedef struct { pmdval_t pgd[2]; } pgd_t;
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
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
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_addr_end
作用:
根据 addr 和 end 获取 ,下一个 addr
声明:
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