学习ARM64页表转换流程
Posted Loopers
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学习ARM64页表转换流程相关的知识,希望对你有一定的参考价值。
在引入虚拟地址概念以后,程序员和CPU看到的都是虚拟地址。当CPU尝试去访问某个虚拟地址的时候,这时候硬件单元MMU就会将此虚拟地址转化为物理地址,然后CPU再去访问。
而在Linux中存储虚拟地址到物理地址转化的关系的表称为页表。
目前最新的linux内核已经支持了5级页表。下图是一个4级页表的转化关系图。
- PGD(Page Global Directory )页全局目录
- PUD(Page Upper Directory)页上级目录
- PMD(Page Middle Directory)页中级目录
- PTE(Page Table Entry) 页表
如果是5级页表的话,会在PGD和PUD之间增加一个level叫P4D。
LINUX目前是支持5级页表,当然也可以通过config(CONFIG_PAGE_LEVELS)去配置的,目前手上的模拟板使用的是三级页表,如果使用三级页表的话,PUD等于PMD。
通过如下一个实例来展示下整个虚拟到物理地址的转化过程。
(struct_task_struct_*)(NSD:0xFFFFFFC5FACA4880)_=_0xFFFFFFC5FACA4880 -> (
thread_info = (flags = 0, padding = (0, 0, 0, 0, 0, 0, 0), addr_limit = 549755813887, preempt_co
state = 1,
stack = 0xFFFFFF8008058000,
usage = (counter = 4),
flags = 1077952768,
ptrace = 0,
wake_entry = (next = 0x0),
on_cpu = 0,
cpu = 5,
虚拟地址是0xFFFFFFC5FACA4880,通过MMU可以看到它对应的物理地址为000000017ACA4880,那虚拟地址到物理地址是如何转化的呢? 我们来详细推到下转化过程
Target_Address_|________________logical|_physical______________|
| C:FFFFFFC5FACA4880| A:000000017ACA4880
是通过MMU看到的一个虚拟地址对应的物理地址。前期条件是目前配置的是3级页表。 目前此地址是线性地址,转化关系比较简单。
270#define virt_to_phys virt_to_phys
271static inline phys_addr_t virt_to_phys(const volatile void *x)
272
273 return __virt_to_phys((unsigned long)(x));
274
248#ifdef CONFIG_DEBUG_VIRTUAL
249extern phys_addr_t __virt_to_phys(unsigned long x);
250extern phys_addr_t __phys_addr_symbol(unsigned long x);
251#else
252#define __virt_to_phys(x) __virt_to_phys_nodebug(x)
253#define __phys_addr_symbol(x) __pa_symbol_nodebug(x)
254#endif
CONFIG_DEBUG_VIRTUAL is not set
235#define __is_lm_address(addr) (!!((addr) & BIT(VA_BITS - 1)))
236
237#define __lm_to_phys(addr) (((addr) & ~PAGE_OFFSET) + PHYS_OFFSET)
238#define __kimg_to_phys(addr) ((addr) - kimage_voffset)
239
240#define __virt_to_phys_nodebug(x) ( \\
241 phys_addr_t __x = (phys_addr_t)(x); \\
242 __is_lm_address(__x) ? __lm_to_phys(__x) : \\
243 __kimg_to_phys(__x); \\
244)
189extern s64 memstart_addr;
190/* PHYS_OFFSET - the physical address of the start of memory. */
191#define PHYS_OFFSET ( VM_BUG_ON(memstart_addr & 1); memstart_addr; )
如果是线性地址的话转化就是:(((addr) & ~PAGE_OFFSET) + PHYS_OFFSET) = 0x000000017ACA4880
对于不是线性地址我们在下节(手动玩转虚拟地址到物理地址转化)举例说明,如何去转化
模拟板目前的配置是虚拟地址位数为39位(VA_BITS=39),页表的大小是4K(CONFIG_ARM64_PAGE_SHIFT=12、CONFIG_ARM64_4K_PAGES=y),页表转化是3级(CONFIG_PAGE_LEVELS=3),所以我们需要详细描述出39位是如何划分的。
内核定义了各级页表索引在虚拟地址中的偏移:
- PGDIR_SHIFT ==> 页全局目录索引的偏移
- P4D_SHIFT ==> 页四级目录索引的偏移
- PUD_SHIFT ==> 页上级目录索引的偏移
- PMD_SHIFT ==> 页中级目录索引的偏移
- PAGE_SHIFT ==> 页表内的偏移
当前模拟板是只有三级页表,则就没有P4D和PUD,这样的话PGD=PMD了。
#msm-4.19/include/asm-generic/pgtable-nop4d.h
typedef struct pgd_t pgd; p4d_t;
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
return (p4d_t *)pgd;
当没有p4d的时候,则pgd就等于p4d
#msm-4.19/include/asm-generic/pgtable-nopud.h
typedef struct p4d_t p4d; pud_t;
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
return (pud_t *)p4d;
当没有pud的时候,pud等于p4d,则pgd=p4d=pud。如下图
接下来就需要确认的是每个level的偏移位是多少,也就是确认PGDIR_SHIFT, PMD_SHIFT, PAGE_SHIFT代码如下:
39/*
40 * Size mapped by an entry at level n ( 0 <= n <= 3)
41 * We map (PAGE_SHIFT - 3) at all translation levels and PAGE_SHIFT bits
42 * in the final page. The maximum number of translation levels supported by
43 * the architecture is 4. Hence, starting at at level n, we have further
44 * ((4 - n) - 1) levels of translation excluding the offset within the page.
45 * So, the total number of bits mapped by an entry at level n is :
46 *
47 * ((4 - n) - 1) * (PAGE_SHIFT - 3) + PAGE_SHIFT
48 *
49 * Rearranging it a bit we get :
50 * (4 - n) * (PAGE_SHIFT - 3) + 3
51 */
52#define ARM64_HW_PGTABLE_LEVEL_SHIFT(n) ((PAGE_SHIFT - 3) * (4 - (n)) + 3)
53
54#define PTRS_PER_PTE (1 << (PAGE_SHIFT - 3))
55
56/*
57 * PMD_SHIFT determines the size a level 2 page table entry can map.
58 */
59#if CONFIG_PGTABLE_LEVELS > 2
60#define PMD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(2)
61#define PMD_SIZE (_AC(1, UL) << PMD_SHIFT)
62#define PMD_MASK (~(PMD_SIZE-1))
63#define PTRS_PER_PMD PTRS_PER_PTE
64#endif
65
66/*
67 * PUD_SHIFT determines the size a level 1 page table entry can map.
68 */
69#if CONFIG_PGTABLE_LEVELS > 3
70#define PUD_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(1)
71#define PUD_SIZE (_AC(1, UL) << PUD_SHIFT)
72#define PUD_MASK (~(PUD_SIZE-1))
73#define PTRS_PER_PUD PTRS_PER_PTE
74#endif
75
76/*
77 * PGDIR_SHIFT determines the size a top-level page table entry can map
78 * (depending on the configuration, this level can be 0, 1 or 2).
79 */
80#define PGDIR_SHIFT ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS)
81#define PGDIR_SIZE (_AC(1, UL) << PGDIR_SHIFT)
82#define PGDIR_MASK (~(PGDIR_SIZE-1))
83#define PTRS_PER_PGD (1 << (VA_BITS - PGDIR_SHIFT))
- PGDIR_SHIFT = ARM64_HW_PGTABLE_LEVEL_SHIFT(4 - CONFIG_PGTABLE_LEVELS) = ARM64_HW_PGTABLE_LEVEL_SHIFT(1)= ((12 - 3) * (3) + 3) = 9*3+3=30
- PMD_SHIFT = ARM64_HW_PGTABLE_LEVEL_SHIFT (2) = (9 * 2 + 3) = 21
- PAGE_SHIFT = 12
以上是关于学习ARM64页表转换流程的主要内容,如果未能解决你的问题,请参考以下文章