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