arm64地址与页表的杂记 一

地址的一些宏定义

CPU不可能一直执行位置无关指令,所以必须将 kernel 本身的虚拟地址(运行地址)映射到它实际所在的物理地址,这个通过为 MMU 创建页表来实现,一旦页表创建完成,并打开了MMU,CPU发出的虚拟地址,可以由 MMU 转换成实际的物理地址,这样CPU执行kernel代码将不再受限。

KIMAGE_VADDR
定义:arch/arm64/include/asm/memory.h
#define KIMAGE_VADDR (MODULES_END)

default 39 if ARM64_VA_BITS_39 为例:

Virtual kernel memory layout:
    modules : 0xffffff8000000000 - 0xffffff8008000000   (   128 MB)
    vmalloc : 0xffffff8008000000 - 0xffffffbdbfff0000   (   246 GB)
      .init : 0xffffff8008c00000 - 0xffffff8008e00000   (  2048 KB)
      .text : 0xffffff8008080000 - 0xffffff8008800000   (  7680 KB)
    .rodata : 0xffffff8008800000 - 0xffffff8008c00000   (  4096 KB)
      .data : 0xffffff8008e00000 - 0xffffff8008e84a00   (   531 KB)
    fixed   : 0xffffffbffe7fd000 - 0xffffffbffec00000   (  4108 KB)
    PCI I/O : 0xffffffbffee00000 - 0xffffffbfffe00000   (    16 MB)
    memory  : 0xffffffc010000000 - 0xffffffc050000000   (  1024 MB)

按 48 bits 理解:
对于ARM64来说,虚拟地址的寻址能力一般是48 bits,当然也支持其他的寻址能力,我们这里按48 bits来作说明。按48 bits的寻址能力,分配给内核的起始虚拟地址是0xffff000000000000,前面会留一部分另做他用,如给KASAN,MODULE模块使用,后面才分给kernel image。我们这里假设不支持KASAN,则给MODULE预留128M的空间,128M以后给kernel image使用,也就是说kernel image的起始地址是0xffff000008000000

KERNEL_START
定义:arch/arm64/include/asm/memory.h
#define KERNEL_START _text

_text定义在链接脚本arch/arm64/kernel/vmlinux.lds.S中,是代码段的起始地址,它的绝对地址是0xffff000008080000。链接脚本定义了kernel image的layout,如果当前PC指针存的是物理地址,对_text相对寻址,可以得到_text的物理地址,也就是kernel image代码段的起始物理地址。

KERNEL_END
定义:arch/arm64/include/asm/memory.h
#define KERNEL_END _end

_end定义在链接脚本中arch/arm64/kernel/vmlinux.lds.S中,是kernel image的结束地址,kernel image出来包括代码段之外,还包括其他段,如BSS段,数据段等等,而_end是整个kernel image的结束地址。

__PHYS_OFFSET
定义:arch/arm64/kernel/head.S
#define __PHYS_OFFSET (KERNEL_START - TEXT_OFFSET)

这个用于计算kernel image的起始地址,起始地址加上TEXT_OFFSET就是代码段的物理地址。
这个地方和 PHYS_OFFSET 区别开来

extern s64                      memstart_addr;
/* PHYS_OFFSET - the physical address of the start of memory. */
#define PHYS_OFFSET             ({ VM_BUG_ON(memstart_addr & 1); memstart_addr; })

创建idmap页表映射

idmap是kernel image中的一个段,其位于 arch/arm64/kernel/vmlinux.lds.S中,定义如下,即定义了一个以__idmap_text_start开始,__idmap_text_end结束的段,该段会被放在vmlinux的代码段中。
 这个段其虚拟地址和物理地址的值相等,其映射关系如下图所示。从图中我们可以看到idmap映射的虚拟地址等于物理地址,因为arm64上物理地址是小于等于48位的,故其虚拟地址就位于0x0000 0000 0000 0000 到0xffff 0000 0000 0000之间,其pgd基地址也相应会被放到ttbr0_el1寄存器中。同时,我们看到它做为kernel image的一部分还会被映射到0xffff 0000 0000 0000和0xffff ffff ffff ffff之间,这个映射关系由后面的swapper映射实现,相应地其pgd的基地址会被放到ttbr1_el1寄存器中。

在这里插入图片描述

之所以需要idmap映射时因为现代处理器存在流水线,分支预测等功能,在MMU开启时,打开MMU指令执行时,其后的指令可能已经取指完成,且其地址还是物理地址。而MMU使能完成后,实际上系统已经运行于虚拟地址模式下,若不采取相应措施,此时这些已经取指完成的指令可能会执行错误。故内核采用了将idmap映射的物理地址和虚拟地址设为相等,从而规避了以上问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ARM架构的Linux页表代码实现主要涉及到以下几个文件: 1. `arch/arm/include/asm/pgtable.h`:定义了ARM架构下的页表相关宏和数据结构,包括页表项(PTE)和页目录项(PMD)的定义。 2. `arch/arm/mm/pgtable.c`:实现了ARM架构下的页表相关函数,包括页表初始化、页表项/页目录项的创建和修改等。 3. `arch/arm/mm/mmu.c`:定义了ARM架构下的内存管理单元(MMU)的初始化和配置函数,包括页表的设置和切换等。 在ARM架构下,页表使用两级结构,包括页目录表(Page Directory Table)和页表(Page Table)。每个级别的表都有对下一级表的指针,最终指向物理内存中的页帧。 ARM架构中的页表项(PTE)和页目录项(PMD)由特定位字段组成,用于保存物理地址、标志位和其他控制信息。页表项和页目录项的结构可以在`arch/arm/include/asm/pgtable.h`中找到。 在ARM架构下,通过`pgd_offset`、`pmd_offset`、`pte_offset`等函数可以计算出对应虚拟地址页表中的索引,并通过这些索引来获取或设置相应的页表项或页目录项。 初始化页表时,可以调用`pgd_alloc`、`pmd_alloc`、`pte_alloc`等函数来创建页表项和页目录项,并通过`pgd_populate`、`pmd_populate`等函数将物理页框与虚拟地址进行映射。 ARM架构中的MMU初始化和配置主要通过`setup_mm_for_reboot`、`init_mmu`和`__create_page_tables`等函数完成。这些函数会设置页表寄存器(TTBR0/TTBR1)和控制寄存器(CRn),从而将页表切换到对应的地址空间。 需要注意的是,不同的ARM架构版本和具体的SoC可能会有一些细微的差异,因此具体的代码分析还需要参考相关的架构文档和代码实现。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值