• Linux内核启动流程-第一阶段汇编流程简介


    一.  Linux启动流程

    看完 Linux 内核的顶层 Makefile 以后再来看 Linux 内核的大致启动流程, Linux 内核的启 动流程要比 uboot 复杂的多,涉及到的内容也更多。
    本文中,我们就大致的了解一下 Linux 核的启动流程。
    要分析 Linux 启动流程,同样需要先编译一下 Linux 源码,因为有很多文件是需要编译才会生成的。

    二.  汇编流程简介

    1.  链接脚本 vmlinux.lds

    首先,分析 Linux 内核的链接脚本文件 arch/arm/kernel/vmlinux.lds ,通过链接脚本可以 找到
    Linux 内核的第一行程序是从哪里执行的。 vmlinux.lds 中有如下代码:
    1. 492 OUTPUT_ARCH(arm)
    2. 493 ENTRY(stext)
    3. 494 jiffies = jiffies_64;
    4. 495 SECTIONS
    5. 496 {
    6. 497 /*
    7. 498 * XXX: The linker does not define how output sections are
    8. 499 * assigned to input sections when there are multiple statements
    9. 500 * matching the same input section name. There is no documented
    10. 501 * order of matching.
    11. 502 *
    12. 503 * unwind exit sections must be discarded before the rest of the
    13. 504 * unwind sections get included.
    14. 505 */
    15. 506 /DISCARD/ : {
    16. 507 *(.ARM.exidx.exit.text)
    17. 508 *(.ARM.extab.exit.text)
    18. 509
    19. ......
    20. 645 }

    493 行的 ENTRY 指明了了 Linux 内核入口,入口为 stext stext 定义在文件 arch/arm/kernel/head.S 中 , 因 此 要 分 析 Linux 内核的启动流程,就得先从文件  arch/arm/kernel/head.S  stext 处开始分析。

    2.  Linux 内核入口 stext

    stext Linux 内核的入口地址,在文件 arch/arm/kernel/head.S 中有如下所示提示内容:

    1. /*
    2. * Kernel startup entry point.
    3. * ---------------------------
    4. *
    5. * This is normally called from the decompressor code. The requirements
    6. * are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
    7. * r1 = machine nr, r2 = atags or dtb pointer.
    8. .....
    9. */

    根据上面 的注释, Linux 内核启动之前要求如下:
    ①、关闭 MMU
    ②、关闭 D-cache
    ③、 I-Cache 无所谓。
    ④、 r0=0
    ⑤、 r1=machine nr( 也就是机器 ID)
    ⑥、 r2=atags 或者设备树 (dtb) 首地址。
    Linux 内核的入口点 stext 其实相当于内核的入口函数, stext 函数内容如下:
    1. 80 ENTRY(stext)
    2. ......
    3. 91 @ ensure svc mode and all interrupts masked
    4. 92 safe_svcmode_maskall r9
    5. 93
    6. 94 mrc p15, 0, r9, c0, c0 @ get processor id
    7. 95 bl __lookup_processor_type @ r5=procinfo r9=cpuid
    8. 96 movs r10, r5 @ invalid processor (r5=0)?
    9. 97 THUMB( it eq ) @ force fixup-able long branch encoding
    10. 98 beq __error_p @ yes, error 'p'
    11. 99
    12. ......
    13. 107
    14. 108 #ifndef CONFIG_XIP_KERNEL
    15. ......
    16. 113 #else
    17. 114 ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
    18. 115 #endif
    19. 116
    20. 117 /*
    21. 118 * r1 = machine no, r2 = atags or dtb,
    22. 119 * r8 = phys_offset, r9 = cpuid, r10 = procinfo
    23. 120 */
    24. 121 bl __vet_atags
    25. ......
    26. 128 bl __create_page_tables
    27. 129
    28. 130 /*
    29. 131 * The following calls CPU specific code in a position independent
    30. 132 * manner. See arch/arm/mm/proc-*.S for details. r10 = base of
    31. 133 * xxx_proc_info structure selected by __lookup_processor_type
    32. 134 * above. On return, the CPU will be ready for the MMU to be
    33. 135 * turned on, and r0 will hold the CPU control register value.
    34. 原子哥在线教学:www.yuanzige.com 论坛:www.openedv.com
    35. 942
    36. I.MX6U 嵌入式 Linux 驱动开发指南
    37. 136 */
    38. 137 ldr r13, =__mmap_switched @ address to jump to after
    39. 138 @ mmu has been enabled
    40. 139 adr lr, BSYM(1f) @ return (PIC) address
    41. 140 mov r8, r4 @ set TTBR1 to swapper_pg_dir
    42. 141 ldr r12, [r10, #PROCINFO_INITFUNC]
    43. 142 add r12, r12, r10
    44. 143 ret r12
    45. 144 1: b __enable_mmu
    46. 145 ENDPROC(stext)

    92 行,调用函数 safe_svcmode_maskall 确保 CPU 处于 SVC 模式,并且关闭了所有的中断。 safe_svcmode_maskall 定义在文件 arch/arm/include/asm/assembler.h 中。
    94 行,读处理器 ID ID 值保存在 r9 寄存器中。
    95 行,调用函数 __lookup_processor_type 检查当前系统是否支持此 CPU ,如果支持就获取 procinfo 信息。 procinfo proc_info_list 类 型 的 结 构 体 , proc_info_list 在文件 arch/arm/include/asm/procinfo.h 中。

    Linux 内核将每种处理器都抽象为一个 proc_info_list 结构体,每种处理器都对应一个 procinfo。因此,可以通过处理器 ID 来找到对应的 procinfo 结构,__lookup_processor_type 函数找 到对应处器的 procinfo 以后会将其保存到 r5 寄存器中。

    128 行,调用函数 __create_page_tables 创建页表。
    137 行,将函数 __mmap_switched 的地址保存到 r13 寄存器中。 __mmap_switched 定义在文件 arch/arm/kernel/head-common.S __mmap_switched 最终会调用 start_kernel 函数。

    144 行 , 调 用 __enable_mmu 函 数 使 能 MMU __enable_mmu 定 义 在 文 件 arch/arm/kernel/head.S 中。 __enable_mmu 最终会通过调用 __turn_mmu_on 来打开 MMU __turn_mmu_on 最后会执行 r13 里面保存的 __mmap_switched 函数。

    3.   __mmap_switched 函数

    __mmap_switched 函数定义在文件 arch/arm/kernel/head-common.S 中,函数代码如下:
    1. 81 __mmap_switched:
    2. 82 adr r3, __mmap_switched_data
    3. 83
    4. 84 ldmia r3!, {r4, r5, r6, r7}
    5. 85 cmp r4, r5 @ Copy data segment if needed
    6. 86 1: cmpne r5, r6
    7. 87 ldrne fp, [r4], #4
    8. 88 strne fp, [r5], #4
    9. 89 bne 1b
    10. 90
    11. 91 mov fp, #0 @ Clear BSS (and zero fp)
    12. 92 1: cmp r6, r7
    13. 93 strcc fp, [r6],#4
    14. 94 bcc 1b
    15. 95
    16. 96 ARM( ldmia r3, {r4, r5, r6, r7, sp})
    17. 97 THUMB( ldmia r3, {r4, r5, r6, r7} )
    18. 98 THUMB( ldr sp, [r3, #16] )
    19. 99 str r9, [r4] @ Save processor ID
    20. 100 str r1, [r5] @ Save machine type
    21. 101 str r2, [r6] @ Save atags pointer
    22. 102 cmp r7, #0
    23. 103 strne r0, [r7] @ Save control register values
    24. 104 b start_kernel
    25. 105 ENDPROC(__mmap_switched)

    104 行最终调用 start_kernel 来启动 Linux 内核, start_kernel 函数定义在文件 init/main.c 中。

  • 相关阅读:
    【华为OD机试真题 python】最长的顺子【2022 Q4 | 200分】
    H3C 存储换盘操作
    MySQL学习笔记14
    Docker启动Java项目报异常:FontConfiguration.getVersion
    一次对BC网站的渗透测试实战
    2022年最新安徽建筑施工信号工(建筑特种作业)考试真题题库及答案
    【Spring源码三千问】@Lazy的替代者ObjectFactory 和 ObjectProvider
    【SpringBoot AOP Redis实现延时双删功能实战】
    淘宝镜像的https证书过期
    walking机器人仿真教程-rosbag录制和播放
  • 原文地址:https://blog.csdn.net/wojiaxiaohuang2014/article/details/133274887