ARM64 MMU 映射

MMU 架构示意

MMU 在 enable 之后, ARM core 对虚拟地址的访问流程

  1. ARM Core 到 MMU 的 TLB 中查找是否有地址转换缓存,若有则返回给ARM Core;
  2. TLB 中没有转换缓存,则到Table walk 保存在 DDR/IRAM 的 translation table;
  3. 在遍历 page table 过程中:
    1. Translation entry 不存在,则报一个 translation fault;
    2. 若页表项存在,检查物理地址对应的page 是否在内存中,若在返回地址;
    3. 若页表项对应物理地址不在内存中,此时产生 page fault, 需要从 disk 中将page 交换到内存
  4. ARM Core 根据返回地址访问地址时,会根据translation table 中指定的 attribute 来确定如何使用 cache 以及 访问权限检查
  5. 最后访问具体地址

Translation table entry 格式

所谓translation table entry 也就是通常我们说的 page table 映射格式。它分为一级和二级。主要描述如下:

一级短描述符描述方式

对于 section, super section 映射方式,使用一级短描述符页表项即可,不需要二级描述符;

对于 page 则需要两级描述符进行描述;

各个类型映射如何区分?

First-level short descriptor 的 bit[1:0] 区分

bit[1:0]

描述

0x00

无效

0x01

Page table

0x1

section/super section table

0x11

section/super section 如果 PXN 开启

二级短描述符标识

二级短描述区分 large page, small page 方式: 根据 Bit[1:0] 区分。

0x01: large page

0x1x: small page

地址转换流程 

arm32:

  • 输入一个虚拟地址:
  • TTBR0 中在高14-N ~ 31 决定 translation base 地址
  • 根据虚拟地址的 bit 10:31-N 和 translation table 的地址决定这个地址在translation table 中偏移量
  • 找到对应的 translation entry

内存属性

ARMV8 的 内存属性分为两大类: normal + device

  1. Normal

主要用于配置内存相关属性,处理器可以对 normal 属性内存 re-order, repeat 和 merge access

  1. device

如其名,是用于外设寄存器配置,分为:

  • Device-nGnRnE: no gathering, no re-order, no early ack, 等同于 Strongly ordered 内存
  • Device-nGnRE: no gathering, no re-order, early ack, 因为会有 early ack 可能会带来一些时序上问题,需要作特殊处理
  • Device-nGRE:no gathering,这个限制又少了一层,增加了 re-order
  • Device-GRE: 最少限制

PageTable

上图是根据 linux 虚拟地址映射的index 架构绘制的ARM64 版本,linux 使用如下几个宏来描述页表信息

pgd

ARM64 使用 低48 bit 来进行描述地址,PGDIR_SHIFT = 39,PTRS_PER_PGD = (1 << 9)

#ifndef pgd_index
/* Must be a compile-time constant, so implement it as a macro */
#define pgd_index(a)  (((a) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))                                                                              
#endif

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

#ifndef pgd_offset
#define pgd_offset(mm, address)     pgd_offset_pgd((mm)->pgd, (address))                                                                       
#endif

pgd_offset : mm->pgd 指向 pgd_t 的数组,它加上 index 即可所引导 va 对应 pgd entry

pgd_index: arm64 使用 va 中 bit[47:39] 这9 个bit 来描述 pgd_index

p4d

/* include/asm-generic/pgtable-nop4d.h */
static inline p4d_t *p4d_offset(pgd_t *pgd, unsigned long address)
{
    return (p4d_t *)pgd;
}

pud

ARM64 使用 VA 的 bit[38:30] 这个 9 个 bit 来描述 pud_index, PTRS_PER_PUD = (1 << 9);

#define __phys_to_virt(x)   ((unsigned long)((x) - PHYS_OFFSET) | PAGE_OFFSET)
#define __va(x)         ((void *)__phys_to_virt((phys_addr_t)(x)))

/* include/linux/pgtable.h */
static inline unsigned long p4d_page_vaddr(p4d_t p4d)
{
    return (unsigned long)__va(p4d_page_paddr(p4d));                                                                                           
}

#ifndef pud_index
static inline unsigned long pud_index(unsigned long address)                                                                                   
{
    return (address >> PUD_SHIFT) & (PTRS_PER_PUD - 1);
}
#define pud_index pud_index
#endif

#ifndef pud_offset                                                                                                                             
static inline pud_t *pud_offset(p4d_t *p4d, unsigned long address)
{
    return (pud_t *)p4d_page_vaddr(*p4d) + pud_index(address);
}
#define pud_offset pud_offset
#endif

 

根据 pud 找到指向 pmd 的数组,然后根据 pmd_index 找到索引,最终组合找到 pmd entry.

pte

ARM64 使用 va 的 bit[11:0] 来作为pte index 索引,PTRS_PER_PTE = (1 << 12);

/* include/linux/pgtable.h */
static inline unsigned long pte_index(unsigned long address)
{    
    return (address >> PAGE_SHIFT) & (PTRS_PER_PTE - 1);
}
#define pte_index pte_index

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

#define pte_offset_map(dir, address)    pte_offset_kernel((dir), (address))
#define pte_unmap(pte) ((void)(pte))    /* NOP */

初始化阶段的 MMU 映射

pagetable 定义地址

/* arch/arm64/kernel/vmlinux.lds.S */
    . = ALIGN(PAGE_SIZE);
    init_pg_dir = .;                                                                                                        
    . += INIT_DIR_SIZE;
    init_pg_end = .;
    
    idmap_pg_dir = .;
    . += IDMAP_DIR_SIZE;
    idmap_pg_end = .;
    
    swapper_pg_dir = .;
    . += PAGE_SIZE

其中 init_pg_dir 是 pgtable 地址

idmap_pg_dir 是 identity map 地址

swapper_pg_dir 是页表映射地址

创建页表

/* arch/arm64/kernel/head.S */
SYM_CODE_START(primary_entry)
    bl  preserve_boot_args
    bl  el2_setup           // Drop to EL1, w0=cpu_boot_mode
    adrp    x23, __PHYS_OFFSET
    and x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0
    bl  set_cpu_boot_mode_flag
    bl  __create_page_tables
    bl  __cpu_setup         // initialise processor
    b   __primary_switch
SYM_CODE_END(primary_entry)

初始化阶段在 prmary_entry 中创建 page table.

SYM_FUNC_START_LOCAL(__create_page_tables)
    mov x28, lr
    adrp    x0, init_pg_dir
    adrp    x1, init_pg_end
    sub x1, x1, x0
    bl  __inval_dcache_area
                                                                                                                            
    /*
     * Clear the init page tables.
     */
    adrp    x0, init_pg_dir
    adrp    x1, init_pg_end
    sub x1, x1, x0
1:  stp xzr, xzr, [x0], #16
    stp xzr, xzr, [x0], #16
    stp xzr, xzr, [x0], #16
    stp xzr, xzr, [x0], #16
    subs    x1, x1, #64
    b.ne    1b
    mov x7, SWAPPER_MM_MMUFLAGS
    
    /*
     * Create the identity mapping.
     */
    adrp    x0, idmap_pg_dir                                                                                                
    adrp    x3, __idmap_text_start      // __pa(__idmap_text_start)
#if (VA_BITS < 48)

#else
    /*
     * If VA_BITS == 48, we don't have to configure an additional
     * translation level, but the top-level table has more entries.
     */
    mov x4, #1 << (PHYS_MASK_SHIFT - PGDIR_SHIFT)
    str_l   x4, idmap_ptrs_per_pgd, x5
#endif
1:
    ldr_l   x4, idmap_ptrs_per_pgd
    mov x5, x3              // __pa(__idmap_text_start)
    adr_l   x6, __idmap_text_end        // __pa(__idmap_text_end)

    map_memory x0, x1, x3, x6, x7, x3, x4, x10, x11, x12, x13, x14

进行页表映射是通过 map_memory 汇编实现的。

.macro map_memory, tbl, rtbl, vstart, vend, flags, phys, pgds, istart, iend, tmp, count, sv
    sub \vend, \vend, #1
    add \rtbl, \tbl, #PAGE_SIZE
    mov \sv, \rtbl
    mov \count, #0
    compute_indices \vstart, \vend, #PGDIR_SHIFT, \pgds, \istart, \iend, \count
    populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
    mov \tbl, \sv
    mov \sv, \rtbl

#if SWAPPER_PGTABLE_LEVELS > 3
    compute_indices \vstart, \vend, #PUD_SHIFT, #PTRS_PER_PUD, \istart, \iend, \count
    populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
    mov \tbl, \sv
    mov \sv, \rtbl
#endif

#if SWAPPER_PGTABLE_LEVELS > 2
    compute_indices \vstart, \vend, #SWAPPER_TABLE_SHIFT, #PTRS_PER_PMD, \istart, \iend, \count                             
    populate_entries \tbl, \rtbl, \istart, \iend, #PMD_TYPE_TABLE, #PAGE_SIZE, \tmp
    mov \tbl, \sv
#endif

    compute_indices \vstart, \vend, #SWAPPER_BLOCK_SHIFT, #PTRS_PER_PTE, \istart, \iend, \count
    bic \count, \phys, #SWAPPER_BLOCK_SIZE - 1
    populate_entries \tbl, \count, \istart, \iend, \flags, #SWAPPER_BLOCK_SIZE, \tmp
    .endm

在分析 map_memory 之前,我们分析下 compute_indices,它是用来计算 vstart 和 vend 对应的 pgtable level 的 index 的,两者之差保存在 count 中;

populate_entries 最终建立指向下一级的映射或者 last level 映射

  .macro populate_entries, tbl, rtbl, index, eindex, flags, inc, tmp1                                                     
.Lpe\@: phys_to_pte \tmp1, \rtbl
    orr \tmp1, \tmp1, \flags    // tmp1 = table entry
    str \tmp1, [\tbl, \index, lsl #3]
    add \rtbl, \rtbl, \inc  // rtbl = pa next level
    add \index, \index, #1
    cmp \index, \eindex
    b.ls    .Lpe\@
    .endm
  • 获取 rtbl 的 pte, 或上 flags保存在 tmp1
  • 将 tmp1 保存在 pgtable 对应 index << 3 的位置处
  • 将 rtbl 指向下一 pg level, index + PGSIZE

Cache 属性更改

对于内存的 memory, 其内存属性支持: memory-normal 和 memory-non-cached,其使用函数是:

/* arch/arm64/include/asm/pgtable.h */
#define pgprot_writecombine(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)

对于 device 类型的属性有:

/* arch/arm64/include/asm/pgtable.h */
#define pgprot_noncached(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE) | PTE_PXN | PTE_UXN)
#define pgprot_device(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_PXN | PTE_UXN)
    
#define pgprot_tagged(prot) \
    __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_TAGGED))
#define pgprot_mhp  pgprot_tagged

对于 device 类型的,有几种属性:

device-nGnRnE : 这个类似 armv7 的 strongly-ordered

device-nGnRE: 支持 gathering, non-re-order, 但是会有 early-ack 功能。

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ARM是一种广泛使用的处理器体系结构,它被用于包括智能手机、平板电脑和其他便携式设备等许多应用中。ARM处理器在执行应用程序时必须访问内存,因此为了保护系统的安全性和稳定性,需要使用内存管理单元(MMU)。 MMU是一种硬件设备,它在CPU执行程序时提供一种虚拟地址空间和实际物理地址空间之间的映射MMU将真实的物理地址与虚拟地址进行映射,这样ARM处理器就可以将指令和数据从内存中读取出来。MMU还提供了一种保护机制,可以在进程之间隔离不同的内存空间,避免各个进程间的干涉。 在ARM体系结构中,MMU有两个关键组成部分:页表和TLB。页表是一种用来存储地址转换信息的数据结构,其中每个页表项存储有关与该虚拟地址相关的物理地址的信息。TLB是一个高速缓存,它存储最近使用的页表项以加速地址转换。 ARM MMU需要进行两种类型的地址转换:虚拟地址到物理地址的转换和背景映射的动态操作。虚拟地址到物理地址的转换通常是根据硬件逻辑中所定义的一组规则执行的。例如,由4KB大小的页面组成的虚拟地址空间可以映射到由4KB大小的页面组成的物理地址空间中。这个转换过程可以通过查询页表来完成。背景映射包括动态地址转换,以及TLB的管理和刷新。例如,内核可以在页表中描述一个虚拟地址范围,并将该范围映射到一个物理地址范围。因为一个进程可能有多个地址空间,所以需要确保只有虚拟地址空间是不同的,物理地址空间应该是隔离的。 总的来说,ARM MMU是一种重要的硬件设备,它提供了一种安全的和可靠的内存管理机制。它通过为每个进程提供独立的地址空间,可以有效地保护系统的安全性和稳定性,同时也为系统提供了强大的性能和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值