01
背景介绍
作者简介 于浩进,linux内核爱好者,现就职于北京灵汐科技有限公司,任职BSP工程师,主要负责IP验证、多媒体驱动开发及一些bring up等工作。 文章大纲 1.背景介绍 2.环境说明 2.1 硬件环境 2.2 Kernel 版本 2.3 kernel 相关配置介绍 3.fixmap 机制介绍 3.1 虚拟空间拓扑 3.2 fixmap 在 early ioremap 应用介绍 3.2.2 __early_ioremap() 3.3 fixmap 在 early console 应用介绍 3.4 fixmap 在 device-tree 应用介绍 3.5 fixmap 在 paging_init 中页表切换介绍 4.小结 5.参考文章 01 背景介绍 Fixmap机制是kernel在启动过程中(start_kernel)临时的映射机制,目的是在真正页表建立之前用于完成对io设备的访问、device-tree的解析以及paging_init中的页表切换等。本文将对该机制做一个深入的分析。 02 环境说明 某SOC芯片,CPU为8核cortex-A53,其DDR物理地址为0x800000000,device-tree存放的物理地址为0x843000000。 4.19.83版本。
以下宏的值,只给出结果了: 03 虚拟空间拓扑 3.1.1 VA=39bit下kernel虚拟地址空间拓扑 图1详细展示了VA=39bit下kernel虚拟地址空间拓扑,里面展示了FIXMAP区域在整个虚拟地址空间所处的位置。 3.1.2 FIXMAP地址空间拓扑 Kernel对Fixmap区域做了进一步的划分,各区间是在enum fixed_addresses 枚举类型定义的(/arch/arm64/include/asm/fixmap.h)。 其各个区间的virtual address通过fix_to_virt(const unsigned int idx)函数获得,其定义是在/include/asm-generic/fixmap.h里面,这个函数后面会用到。 下图2详细展示了各个区间的base address。 Fix_to_virt的定义如下: 3.1.3 FIXMAP初始化 Bm_pte、bm_pmd、Bm_pte为三个全局数组,用于暂存pud、pmd、 pte的页表。 early_fixmap_init()函数完成了fixmap映射的基础框架,如下图3所示,bm_pte数组并没有填值,因为当前还不知道哪些物理地址需要映射,等需要映射时候再去填写bm_pte的entry。 经过分析代码,整理了fixmap各段虚拟地址与bm_pmd entry的关系,如下图4所示: 需要说明的是FIX_PGD~FIX_FDT在bm_pmd是属于同一个entry,即可以用bm_pte做pte映射。 FIX_FDT~FIX_HOLE不属于该entry,即不可以用bm_pte做pte映射,也为后面device-tree的映射做了一个铺垫。 3.2.1 early_ioremap_setup() 该函数的比较简单,主要是依靠__fix_to_virt()给slot_virt[i]填入虚拟地址,其布局如下图5所示。 slot[i]是fix_map区域已经规划好的虚拟地址范围,任何I/O地址空间都可以向这7个slot空间做映射。 其中:slot_virt[i] = __fix_to_virt(FIX_BTMAP_BEGIN - NR_FIX_BTMAPS*i),__fix_to_virt()在之前已经介绍过。Slot_virt每个区间size为256K。 3.2.2 __early_ioremap() 有三个数组需要说明: slot_virt[slot]:BTMAP区域各个区间虚拟地址; prev_map[slot]:__early_ioremap()映射后的虚拟地址; prev_size[slot]:__early_ioremap()要映射的size; 映射流程如下图6所示: Figure 6 early ioremap映射流程图 图7展示了early ioremap页表转换过程,还是比较简单的。 Early console的映射与early ioremap的映射类似,通过__fix_to_vit(FIX_EARLYCON_MEM_BASE)获取虚拟地址,物理地址为UART在SOC的实际分配的地址(该物理地址来自于command line的earlycon=XXX),然后通过向bm_pte写入页表,即可以完成映射。 图8是函数调用关系。图9是页表的建立和转换过程。 3.4.1 映射过程分析 Device-tree的映射和early-console、early-ioremap的映射原理有所不同,主要区别在于FIX_FDT空间对应的虚拟地址的pmd entry与FIXADDR_START对应的pmd entry是不同的。 通过分析kernel代码可知对于device-tree的映射需要建立一个2M的block entry即可,即在bm_pmd建立一个block entry。 如下图10所示,只需要找到pmdp,写入block entry的页表项即可。 那问题来了,pmdp的虚拟地址我们是知道的,对应的bm_pmd的entry的物理地址也能知道,但是两者之间的页表还未建立。 因此在用pmdp指针向bm_pmd写入block entry之前,必须要先建立pmdp的页表,这个页表建立过程就与early console的页表建立过程相同了。见下图11所示。 设备树页表的建立会调用到init_pmd()建立block entry,也就是下图12圈2对应的代码,圈1的代码就是对应上图11给pmdp建立页表的过程。 在写入block entry之后,pmdp也就无用了,圈3代码把刚才的pmdp的页表清除了,即把bm_pte对应的表项清除了。 最后再简单展示一下fixmap为设备树建立页表的函数调用关系,如下图13所示。 3.5.1 paging_init函数简单分析 下图14是paging_init的代码分析。
3.5.2 paging_init中的fixmap 上图 圈6代码是把临时页表拷贝到swapper_pg_dir,临时页表的物理页是memblock分配器获得的物理地址。 由于mmu已经开启,memcpy无法使用物理地址,所以必须要先用fixmap机制做该物理页面的映射,得到其虚拟地址,即pgd_set_fixmap(addr),其定义如下: 其是借助于fixmp的“FIX_PGD”区域做的映射,页表映射及转换过程如下图15所示。 04 小结 ![]()
05 参考文献 ![]() https://www.cnblogs.com/LoyenWang/p/11483948.html https://www.cnblogs.com/LoyenWang/p/11440957.html https://www.cnblogs.com/pengdonglin137/p/9157639.html armv8_arm.pdf,从ARM官网下载即可。 ![]() |
|