鸿蒙内核源码分析 (内核启动篇) | 从汇编到 main ()

[复制链接]
发表于 2026-2-19 19:35:29 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
这应该是系列篇最难写的一篇,满是汇编代码,需大量的底层知识,涉及协处置处罚器,内核镜像重定位,创建内核映射表,初始化 CPU 模式栈,热启动,到末了熟悉的 main()
内核入口

在链接文件 liteos.ld 中可知内核的入口地点为 ENTRY(reset_vector) , 分别出如今reset_vector_mp.S (多核启动) 和 reset_vector_up.S(单核启动),系列篇研究多核启动的环境。代码可联合 (协处置处罚器篇) 看更容易懂。
  1. reset_vector: //鸿蒙开机代码
  2.     /* clear register TPIDRPRW */
  3.     mov     r0, #0                                        //r0 = 0
  4.     mcr     p15, 0, r0, c13, c0, 4        //复位线程标识符寄存器TPIDRPRW , 不复位将导致系统不能启动
  5.     /* do some early cpu setup: i/d cache disable, mmu disabled */
  6.     mrc     p15, 0, r0, c1, c0, 0        //System Control Register-SCTLR | 读取系统控制寄存器内容
  7.     bic     r0, #(1<<12)                        //禁用指令缓存功能
  8.     bic     r0, #(1<<2 | 1<<0)                //禁用数据和TLB的缓存功能(bit2) | mmu功能(bit0)
  9.     mcr     p15, 0, r0, c1, c0, 0        //写系统控制寄存器
  10.     /* enable fpu+neon 一些系统寄存器的操作
  11.     | 使能浮点运算(floating point unit)和 NEON就是一种基于SIMD思想的ARM技术,相比于ARMv6或之前的架构,
  12.     NEON结合了64-bit和128-bit的SIMD指令集,提供128-bit宽的向量运算(vector operations)*/
  13. #ifndef LOSCFG_TEE_ENABLE        //Trusted Execution Environment   可信执行环境
  14.     MRC    p15, 0, r0, c1, c1, 2 //非安全模式访问寄存器 (Non-Secure Access Control Register - NSACR)
  15.     ORR    r0, r0, #0xC00        //使能安全和非安全访问协处理器10和11(Coprocessor 10和11)
  16.     BIC    r0, r0, #0xC000       //设置bit15为0,不会影响修改CPACR.ASEDIS寄存器位(控制Advanced SIMD功能)| bit14 reserved
  17.     MCR    p15, 0, r0, c1, c1, 2
  18.     LDR    r0, =(0xF << 20)      //允许在EL0和EL1下,访问协处理器10和11(控制Floating-point和Advanced SIMD特性)
  19.     MCR    p15, 0, r0, c1, c0, 2
  20.     ISB
  21. #endif
  22.     MOV    r3, #0x40000000            //EN, bit[30] 设置FPEXC的EN位来使能FPU
  23.     VMSR   FPEXC, r3                        //浮点异常控制寄存器 (Floating-Point Exception Control register | B4.1.57)
  24.     /* r11: delta of physical address and virtual address | 计算虚拟地址和物理地址之间的差值,目的是为了建立映射关系表 */
  25.     adr     r11, pa_va_offset //获取pa_va_offset变量物理地址,由于这时候mmu已经被关闭,所以这个值就表示pa_va_offset变量的物理地址。
  26.                               /*adr 是一条小范围的地址读取伪指令,它将基于PC的相对偏移的地址值读到目标寄存器中。
  27.                                *编译源程序时,汇编器首先计算当前PC值(当前指令位置)到exper的距离,然后用一条ADD或者SUB指令替换这条伪指令,
  28.                                *例如:ADD register,PC,#offset_to_exper 注意,标号exper与指令必须在同一代码段
  29.                                */
  30.     ldr     r0, [r11]                  //r0 = *r11 获取pa_va_offset变量虚拟地址
  31.     sub     r11, r11, r0          //物理地址-虚拟地址 = 映射偏移量 放入r11
  32.     mrc     p15, 0, r12, c0, c0, 5      /* Multiprocessor Affinity Register-MPIDR */
  33.     and     r12, r12, #MPIDR_CPUID_MASK //掩码过滤
  34.     cmp     r12, #0                            //主控核0判断
  35.     bne     secondary_cpu_init                //初始化CPU次核
  36.         /*
  37.          * adr是小范围的地址读取伪指令,它将基于PC寄存器相对偏移的地址值读取到寄存器中,
  38.          * 例如: 0x00000004          : adr     r4, __exception_handlers
  39.          * 则此时PC寄存器的值为: 0x00000004 + 8(在三级流水线时,PC和执行地址相差8),
  40.      * adr指令和标识__exception_handlers的地址相对固定,二者偏移量若为offset,
  41.          * 最后r4 = (0x00000004 + 8) + offset
  42.         */
  43.     /* if we need to relocate to proper location or not | 如果需要重新安装到合适的位置*/
  44.     adr     r4, __exception_handlers            /* r4: base of load address | 加载基址*/
  45.     ldr     r5, =SYS_MEM_BASE                   /* r5: base of physical address | 物理基址*/
  46.     subs    r12, r4, r5                         /* r12: delta of load address and physical address | 二者偏移量*/
  47.     beq     reloc_img_to_bottom_done            /* if we load image at the bottom of physical address | 不相等就需要重定位 */
  48.     /* we need to relocate image at the bottom of physical address | 需要知道拷贝的大小*/
  49.     ldr     r7, =__exception_handlers           /* r7: base of linked address (or vm address) | 链接地址基地址*/
  50.     ldr     r6, =__bss_start                    /* r6: end of linked address (or vm address),由于目前阶段有用的数据是中断向量表+代码段+只读数据段+数据段,
  51.                                                                                                所以只需复制[__exception_handlers,__bss_start]这段数据到内存基址处 */
  52.     sub     r6, r7                              /* r6: delta of linked address (or vm address) | 内核镜像大小 */
  53.     add     r6, r4                              /* r6: end of load address | 说明需拷贝[ r4,r4+r6 ] 区间内容到 [ r5,r5+r6 ]*/
  54. reloc_img_to_bottom_loop://重定位镜像到内核物理内存基地址,将内核从加载地址拷贝到内存基址处
  55.     ldr     r7, [r4], #4        // 类似C语言 *r5 = *r4 , r4++ , r5++
  56.     str     r7, [r5], #4        // #4 代表32位的指令长度,此时在拷贝内核代码区内容
  57.     cmp     r4, r6          /* 拷贝完成条件. r4++ 直到等于r6 (加载结束地址) 完成拷贝动作 */
  58.     bne     reloc_img_to_bottom_loop
  59.     sub     pc, r12                             /* 重新校准pc寄存器, 无缝跳到了拷贝后的指令地址处执行 r12是重定位镜像前内核加载基地址和内核物理内存基地址的差值 */
  60.     nop                // 注意执行完成sub       pc, r12后,新的PC寄存器也指向了         nop ,nop是伪汇编指令,等同于 mov r0 r0 通常用于控制时序的目的,强制内存对齐,防止流水线灾难,占据分支指令延迟                                               
  61.     sub     r11, r11, r12                       /* r11: eventual address offset | 最终地址映射偏移量, 用于构建MMU页表 */
  62. //内核总大小 __bss_start - __exception_handlers
  63. reloc_img_to_bottom_done:
  64. #ifdef LOSCFG_KERNEL_MMU
  65.     ldr     r4, =g_firstPageTable               /* r4: physical address of translation table and clear it
  66.                                                                                                    内核页表是用数组g_firstPageTable存储 见于los_arch_mmu.c */
  67.     add     r4, r4, r11                         //计算g_firstPageTable页表物理地址
  68.     mov     r0, r4                                                                //因为默认r0 将作为memset_optimized的第一个参数
  69.     mov     r1, #0                                                                //第二个参数,清0
  70.     mov     r2, #MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS //第三个参数是L1表的长度
  71.     bl      memset_optimized                    /* optimized memset since r0 is 64-byte aligned | 将内核页表空间清零*/
  72.     ldr     r5, =g_archMmuInitMapping                //记录映射关系表
  73.     add     r5, r5, r11                         //获取g_archMmuInitMapping的物理地址
  74. init_mmu_loop:                                        //初始化内核页表
  75.     ldmia   r5!, {r6-r10}                       /* r6 = phys, r7 = virt, r8 = size, r9 = mmu_flags, r10 = name | 传参: 物理地址、虚拟地址、映射大小、映射属性、名称*/
  76.     cmp     r8, 0                               /* if size = 0, the mmu
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表