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 init done | 完成条件 */
beq init_mmu_done //标志寄存器中Z标志位等于零时跳转到 init_mmu_done处执行
bl page_table_build //创建页表
b init_mmu_loop //循环继续
init_mmu_done:
orr r8, r4, #MMU_TTBRx_FLAGS /* r8 = r4 and set cacheable attributes on translation walk | 设置缓存*/
ldr r4, =g_mmuJumpPageTable /* r4: jump pagetable vaddr | 页表虚拟地址*/
add r4, r4, r11
ldr r4, [r4]
add r4, r4, r11 /* r4: jump pagetable paddr | 页表物理地址*/
/* build 1M section mapping, in order to jump va during turing on mmu:pa == pa, va == pa */
/* 从当前PC开始建立1MB空间的段映射,分别建立物理地址和虚拟地址方式的段映射页表项
* 内核临时页表在系统 使能mmu -> 切换到虚拟地址运行 这段时间使用
*/
mov r6, pc
mov r7, r6 /* r7: pa (MB aligned)*/
lsr r6, r6, #20 /* r6: pa l1 index */
ldr r10, =MMU_DESCRIPTOR_KERNEL_L1_PTE_FLAGS
add r12, r10, r6, lsl #20 /* r12: pa |flags */
str r12, [r4, r7, lsr #(20 - 2)] /* jumpTable[paIndex] = pt entry */
rsb r7, r11, r6, lsl #20 /* r7: va */
str r12, [r4, r7, lsr #(20 - 2)] /* jumpTable[vaIndex] = pt entry */
bl mmu_setup /* set up the mmu | 内核映射表已经创建好了,此时可以启动MMU工作了*/
#endif
/* clear out the interrupt and exception stack and set magic num to check the overflow
|exc_stack|地址高位
|svc_stack|地址低位
清除中断和异常堆栈并设置magic num检查溢出 */
ldr r0, =__svc_stack //stack_init的第一个参数 __svc_stack表示栈顶
ldr r1, =__exc_stack_top //stack_init的第二个参数 __exc_stack_top表示栈底, 这里会有点绕, top表高地址位
bl stack_init //初始化各个cpu不同模式下的栈空间
//设置各个栈顶魔法数字
STACK_MAGIC_SET __svc_stack, #OS_EXC_SVC_STACK_SIZE, OS_STACK_MAGIC_WORD //中断栈底设成"烫烫烫烫烫烫"
STACK_MAGIC_SET __exc_stack, #OS_EXC_STACK_SIZE, OS_STACK_MAGIC_WORD //异常栈底设成"烫烫烫烫烫烫"
warm_reset: //热启动 Warm Reset, warm reboot, soft reboot, 在不关闭电源的情况,由软件控制重启计算机
/* initialize CPSR (machine state register) */
mov r0, #(CPSR_IRQ_DISABLE|CPSR_FIQ_DISABLE|CPSR_SVC_MODE) /* 禁止IRQ中断 | 禁止FIQ中断 | 管理模式-操作系统使用的保护模式 */
msr cpsr, r0 //设置CPSR寄存器
/* Note: some functions in LIBGCC1 will cause a "restore from SPSR"!! */
msr spsr, r0 //设置SPSR寄存器
/* get cpuid and keep it in r12 */
mrc p15, 0, r12, c0, c0, 5 //R12保存CPUID
and r12, r12, #MPIDR_CPUID_MASK //掩码操作获取当前cpu id
/* set svc stack, every cpu has OS_EXC_SVC_STACK_SIZE stack | 设置 SVC栈 */
ldr r0, =__svc_stack_top //注意这是栈底,高地址位
mov r2, #OS_EXC_SVC_STACK_SIZE //栈大小
mul r2, r2, r12
sub r0, r0, r2 /* 算出当前core的中断栈栈顶位置,写入所属core的sp */
mov sp, r0
LDR r0, =__exception_handlers
MCR p15, 0, r0, c12, c0, 0 /* Vector Base Address Register - VBAR */
cmp r12, #0 //CPU是否为主核
bne cpu_start //不相等就跳到从核处理分支
clear_bss: //主核处理.bss段清零
ldr r0, =__bss_start
ldr r2, =__bss_end
mov r1, #0
sub r2, r2, r0
bl memset
#if defined(LOSCFG_CC_STACKPROTECTOR_ALL) || \
defined(LOSCFG_CC_STACKPROTECTOR_STRONG) || \
defined(LOSCFG_CC_STACKPROTECTOR)
bl __stack_chk_guard_setup
#endif
#ifdef LOSCFG_GDB_DEBUG
/* GDB_START - generate a compiled_breadk,This function will get GDB stubs started, with a proper environment */
bl GDB_START
.word 0xe7ffdeff
#endif
bl main //带LR的子程序跳转, LR = pc - 4, 执行C层main函数