📚鸿蒙开发往期学习笔录📌:
📌 鸿蒙应用开发与鸿蒙系统开发哪个更有前景?
📌 嵌入式开发适不适合做鸿蒙南向开发?看完这篇你就了解了~
📌 对于大前端开发来说,转鸿蒙开发究竟是福还是祸?
📌 鸿蒙岗位需求突增!移动端、PC端、IoT到底该怎么选?
📌 记录一场鸿蒙开发岗位面试经历~
📌 持续更新中……
这应该是系列篇最难写的一篇,全是汇编代码,需大量的底层知识,涉及协处理器,内核镜像重定位,创建内核映射表,初始化 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 ]*/