前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习ARM64页表转换流程

学习ARM64页表转换流程

作者头像
DragonKingZhu
发布2020-04-30 18:16:12
2.1K0
发布2020-04-30 18:16:12
举报

在引入虚拟地址概念以后,程序员和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
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-04-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 对于不是线性地址我们在下节(手动玩转虚拟地址到物理地址转化)举例说明,如何去转化
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档