你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

linux在启动过程中的初始化

[复制链接]
gaosmile 发布时间:2020-6-7 18:29
看了很多关于linux内存管理的文章还是云里雾里,听了很多关于linux内存管理的课程还是一头雾水。其实很多时候造成不懂的原因不是资料太少,恰恰是资料太多,而且各个内核版本的差异,32位64位的不同,文章的胡编乱造等都给读者带来疑惑。本着对内存深度剖析的态度,希望以版本kernel-4.14,架构AARCH64为专题做个内存管理的架构性整理。
这篇文章我们先来看下linux在启动过程中的初始化。

创建启动页表:
在汇编代码阶段的head.S文件中,负责创建映射关系的函数是create_page_tables。create_page_tables函数负责identity mapping和kernel image mapping。
  • identity map:是指把idmap_text区域的物理地址映射到相等的虚拟地址上,这种映射完成后,其虚拟地址等于物理地址。idmap_text区域都是一些打开MMU相关的代码。
  • kernel image map:将kernel运行需要的地址(kernel txt、rodata、data、bss等等)进行映射。

    1. arch/arm64/kernel/head.S:
    2. ENTRY(stext)
    3.         bl      preserve_boot_args
    4.         bl      el2_setup                       // Drop to EL1, w0=cpu_boot_mode
    5.         adrp    x23, __PHYS_OFFSET
    6.         and     x23, x23, MIN_KIMG_ALIGN - 1    // KASLR offset, defaults to 0
    7.         bl      set_cpu_boot_mode_flag
    8.         bl      __create_page_tables
    9.         /*
    10.          * The following calls CPU setup code, see arch/arm64/mm/proc.S for
    11.          * details.
    12.          * On return, the CPU will be ready for the MMU to be turned on and
    13.          * the TCR will have been set.
    14.          */
    15.         bl      __cpu_setup                     // initialise processor
    16.         b       __primary_switch
    17. ENDPROC(stext)
    复制代码

__create_page_tables主要执行的就是identity map和kernel image map:

  1. __create_page_tables:
  2. ......
  3.         create_pgd_entry x0, x3, x5, x6
  4.         mov     x5, x3                          // __pa(__idmap_text_start)
  5.         adr_l   x6, __idmap_text_end            // __pa(__idmap_text_end)
  6.         create_block_map x0, x7, x3, x5, x6

  7.         /*
  8.          * Map the kernel image (starting with PHYS_OFFSET).
  9.          */
  10.         adrp    x0, swapper_pg_dir
  11.         mov_q   x5, KIMAGE_VADDR + TEXT_OFFSET  // compile time __va(_text)
  12.         add     x5, x5, x23                     // add KASLR displacement
  13.         create_pgd_entry x0, x5, x3, x6
  14.         adrp    x6, _end                        // runtime __pa(_end)
  15.         adrp    x3, _text                       // runtime __pa(_text)
  16.         sub     x6, x6, x3                      // _end - _text
  17.         add     x6, x6, x5                      // runtime __va(_end)
  18.         create_block_map x0, x7, x3, x5, x6
  19. ......
复制代码

其中调用create_pgd_entry进行PGD及所有中间level(PUD, PMD)页表的创建,调用create_block_map进行PTE页表的映射。关于四级页表的关系如下图所示,这里就不进一步解释了。
微信图片_20200607182745.jpg

汇编结束后的内存映射关系如下图所示:
微信图片_20200607182749.jpg

当执行完上面的map之后,MMU就已经打开了并且开始进入C代码运行阶段,那么下一步就要对dtb进行映射了。

fixmap区之dtb map:
微信图片_20200607182752.jpg
在执行setup_arch中,会最先进行early_fixmap_init(),这个函数就是用来map dtb的,但是它只会建立dtb对应的这段物理地址中间level的页表entry,而最后一个level的页表映射则通过setup_machine_fdt函数里的fixmap_remap_fdt来创建。
  1. <font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
  2. void *__init fixmap_remap_fdt(phys_addr_t dt_phys)
  3. {
  4.      void *dt_virt;
  5.      int size;

  6.      dt_virt = __fixmap_remap_fdt(dt_phys, &size, PAGE_KERNEL_RO);
  7.      if (!dt_virt)
  8.          return NULL;

  9.      memblock_reserve(dt_phys, size);
  10.      return dt_virt;
  11. }</font></font></font><font color="#001000"><font style="background-color:rgb(255, 255, 255)"><font face="Tahoma">
  12. </font></font></font>
复制代码

fixmap_remap_fdt主要是为fdt建立地址映射,在该函数的最后,顺便就调用memblock_reserve保留了该段内存。
可以看出dtb的映射采用的是fixmap,所谓fixmap就是固定映射,它需要我们明确的知道想要映射的物理地址,并把这段地址映射到想要映射的虚拟地址上。当然这里固定映射还有些片面,因为在fixmap机制实现上,也有支持动态分配虚拟地址的功能,这个功能主要用于临时fixmap映射(这个临时映射就是用来执行early ioremap使用的。),而dtb的映射属于永久映射。

fixmap区之early ioremap:
对于一些硬件需要在内存管理系统起来之前就要工作的,我们就可以使用这种机制来映射内存给这些硬件driver使用。各个模块在使用完early ioremap的地址后,需要尽快把这段映射的虚拟地址释放掉,这样才能反复被其他模块继续申请使用。
early_ioremap_init会调用early_ioremap_setup:
微信图片_20200607182755.png
可见它的实现是依赖fixmap的,所以它必须要在early_fixmap_init之后才能运行。
注意:如果想要在伙伴系统初始化之前进行设备寄存器的访问,那么可以考虑early IO remap机制。
至此我们已经知道dtb和early ioremap都是在fixmap区的,如下图:
微信图片_20200607182758.jpg

系统内存的布局:
完成dtb的map之后,内核可以访问这一段的内存了,通过解析dtb中的内容,内核可以勾勒出整个内存布局的情况,为后续内存管理初始化奠定基础。这一步主要在setup_machine_fdt中完成。这里就不看代码了,其调用流程是:setup_machine_fdt->early_init_dt_scan->early_init_dt_scan_nodes
微信图片_20200607182802.png
就像注释中所示内核根据dtb的不同node勾勒出choosen node,root node,memory node相应内存区域。
除了这3个node,还有一个reserved-memory node,它是在上面讲到dtb map的时候fixmap_remap_fdt函数做的。下面我们看下这4个node的具体实现。
  • choosen node

微信图片_20200607182805.png
该节点有一个bootargs属性,该属性定义了内核的启动参数,比如mem= xx,此外,还处理initrd相关的property,并保存在initrd_start和initrd_end这两个全局变量中。
  • root node

与内存无关,暂时不详述,以后有机会讲到device tree系列再详述。
  • memory node

微信图片_20200607182809.png
通过memblock_add加入到memblock.memory对应的memblock_type链表中进行管理。

接下来到arm64_memblock_init函数:

  1. void __init arm64_memblock_init(void)
  2. {
  3. ......
  4.      memblock_reserve(__pa_symbol(_text), _end - _text); 1.kernel image保留区
  5. #ifdef CONFIG_BLK_DEV_INITRD
  6.      if (initrd_start) {
  7.          memblock_reserve(initrd_start, initrd_end - initrd_start); 2.initrd保留区
  8.          /* the generic initrd code expects virtual addresses */
  9.          initrd_start = __phys_to_virt(initrd_start);
  10.          initrd_end = __phys_to_virt(initrd_end);
  11.      }
  12. #endif
  13.      early_init_fdt_scan_reserved_mem(); 3.dts中配置为保留的区域
  14. ......
  15. }
复制代码

  • reserve内核代码、数据区等(_text到_end那一段,具体的内容可以参考内核链接脚本)
  • 保留initital ramdisk image区域(从initrd_start到initrd_end区域)
  • reserved-memory node 如下所示:

微信图片_20200607182812.jpg

完成:
通过上面的一系列操作,需要动态管理的内存已经被放到了memory type和reserved type这两个region中了,现在内存已经被memblock模块所管理了,这只是启动后的第一步,后续内存才会加入到伙伴系统去管理。

收藏 评论0 发布时间:2020-6-7 18:29

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版