提交 2bac2a79 编写于 作者: 鸿蒙内核源码分析's avatar 鸿蒙内核源码分析

对开机汇编代码详细注解

    百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
    鸿蒙研究站 | http://weharmonyos.com (国内)
              | https://weharmony.github.io (国外)
    oschina | https://my.oschina.net/weharmony
    博客园 | https://www.cnblogs.com/weharmony/
    知乎 | https://www.zhihu.com/people/weharmonyos
    csdn | https://blog.csdn.net/kuangyufei
    51cto | https://harmonyos.51cto.com/column/34
    掘金 | https://juejin.cn/user/756888642000808
    公众号 | 鸿蒙研究站 (weharmonyos)
上级 9c73f63c
......@@ -119,7 +119,7 @@
/* Initial bit32 stack value. */
#define OS_STACK_INIT 0xCACACACA ///< 栈指针初始化值 0b 1010 1010 1010
/* Bit32 stack top magic number. */
#define OS_STACK_MAGIC_WORD 0xCCCCCCCC ///< 用于栈值,可标识为栈是否被溢出过,神奇的 "烫烫烫烫"的根源所在! 0b 1100 1100 1100
#define OS_STACK_MAGIC_WORD 0xCCCCCCCC ///< 用于栈值,可标识为栈是否被溢出过,神奇的 "烫烫烫烫"的根源所在! 0b 1100 1100 1100
/**
* @brief
......
......@@ -97,7 +97,7 @@ __attribute__((aligned(MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS))) \
g_tempPageTable[MMU_DESCRIPTOR_L1_SMALL_ENTRY_NUMBERS];
UINT8 *g_mmuJumpPageTable = g_tempPageTable;
#else
extern CHAR __mmu_ttlb_begin; /* defined in .ld script */
extern CHAR __mmu_ttlb_begin; /* defined in .ld script | 内核临时页表在系统使能mmu到使用虚拟地址运行这段期间使用,其虚拟地址保存在g_mmuJumpPageTable这个指针中*/
UINT8 *g_mmuJumpPageTable = (UINT8 *)&__mmu_ttlb_begin; /* temp page table, this is only used when system power up | 临时页表,用于系统启动阶段*/
#endif
/// 获取页表基地址
......
......@@ -112,9 +112,9 @@ LDM/STR架构中{∧}为可选后缀,当指令为LDM且寄存器列表中包含R
/* macros to align and unalign the stack on 8 byte boundary for ABI compliance */
.macro STACK_ALIGN, reg /* 栈对齐*/
MOV \reg, sp @reg=sp
TST SP, #4 @来检查是否设置了特定的位
SUBEQ SP, #4 @表示相等时相减
MOV \reg, sp //reg=sp
TST SP, #4 //来检查是否设置了特定的位
SUBEQ SP, #4 //表示相等时相减
PUSH { \reg }
.endm
......@@ -125,28 +125,28 @@ LDM/STR架构中{∧}为可选后缀,当指令为LDM且寄存器列表中包含R
/* macros to save and restore fpu regs */
.macro PUSH_FPU_REGS reg1 /* 保存fpu寄存器 */
#if !defined(LOSCFG_ARCH_FPU_DISABLE) @FPU使能
#if !defined(LOSCFG_ARCH_FPU_DISABLE) //FPU使能
VMRS \reg1, FPEXC
PUSH {\reg1} @对应 TaskContext->regFPEXC
PUSH {\reg1} //对应 TaskContext->regFPEXC
VMRS \reg1, FPSCR
PUSH {\reg1} @对应 TaskContext->regFPSCR
PUSH {\reg1} //对应 TaskContext->regFPSCR
#if defined(LOSCFG_ARCH_FPU_VFP_D32)
VPUSH {D16-D31} @对应 TaskContext->D
VPUSH {D16-D31} //对应 TaskContext->D
#endif
VPUSH {D0-D15} @对应 TaskContext->D
VPUSH {D0-D15} //对应 TaskContext->D
#endif
.endm
.macro POP_FPU_REGS reg1 /* 恢复fpu寄存器 */
#if !defined(LOSCFG_ARCH_FPU_DISABLE)
VPOP {D0-D15} @对应 TaskContext->D
VPOP {D0-D15} //对应 TaskContext->D
#if defined(LOSCFG_ARCH_FPU_VFP_D32)
VPOP {D16-D31} @对应 TaskContext->D
VPOP {D16-D31} //对应 TaskContext->D
#endif
POP {\reg1}
VMSR FPSCR, \reg1 @对应 TaskContext->regFPSCR
VMSR FPSCR, \reg1 //对应 TaskContext->regFPSCR
POP {\reg1}
VMSR FPEXC, \reg1 @对应 TaskContext->regFPEXC
VMSR FPEXC, \reg1 //对应 TaskContext->regFPEXC
#endif
.endm
......@@ -171,17 +171,17 @@ OsTaskSchedule: /*任务调度,OsTaskSchedule的目的是将寄存器值按Tas
PUSH_FPU_REGS R2 /*保存fpu寄存器*/
/* store sp on running task */
STR SP, [R1] @在运行的任务栈中保存SP,*runTask->stackPointer = sp
STR SP, [R1] //在运行的任务栈中保存SP,*runTask->stackPointer = sp
OsTaskContextLoad: @加载上下文
/* clear the flag of ldrex */ @LDREX 可从内存加载数据,如果物理地址有共享TLB属性,则LDREX会将该物理地址标记为由当前处理器独占访问,并且会清除该处理器对其他任何物理地址的任何独占访问标记。
CLREX @清除ldrex指令的标记
OsTaskContextLoad: //加载上下文
/* clear the flag of ldrex */ //LDREX 可从内存加载数据,如果物理地址有共享TLB属性,则LDREX会将该物理地址标记为由当前处理器独占访问,并且会清除该处理器对其他任何物理地址的任何独占访问标记。
CLREX //清除ldrex指令的标记
/* switch to new task's sp */
LDR SP, [R0] @ 即:sp = *task->stackPointer
LDR SP, [R0] // 即:sp = *task->stackPointer
/* restore fpu registers */
POP_FPU_REGS R2 @恢复fpu寄存器,这里用了汇编宏R2是宏的参数
POP_FPU_REGS R2 //恢复fpu寄存器,这里用了汇编宏R2是宏的参数
LDMFD SP!, {R4-R11}
LDR R3, [SP, #(11 * 4)]
......@@ -204,16 +204,16 @@ OsTaskContextLoad: @加载上下文
LDMFD SP!, {R0-R3, R12, LR}
RFEIA SP!
OsKernelTaskLoad: @内核任务的加载
OsKernelTaskLoad: //内核任务的加载
ADD SP, SP, #(4 * 4)
LDMFD SP!, {R0-R3, R12, LR}
RFEIA SP!
OsIrqHandler:
SUB LR, LR, #4 @记录译码指令地址,以防切换过程丢失指令.
SUB LR, LR, #4 //记录译码指令地址,以防切换过程丢失指令.
/* Save pc and cpsr to svc sp, ARMv6 and above support */
SRSFD #0x13!
/* disable irq, switch to svc mode */@超级用户模式(SVC 模式),主要用于 SWI(软件中断) OS(操作系统)
/* disable irq, switch to svc mode *///超级用户模式(SVC 模式),主要用于 SWI(软件中断) OS(操作系统)
CPSID i, #0x13
#ifdef LOSCFG_KERNEL_PERF
PUSH {R0-R3, R12, LR}
......@@ -272,37 +272,37 @@ OsIrqHandler:
LDMFD SP!, {R0-R3, R12, LR}
RFEIA SP!
FUNCTION(ArchSpinLock) @非要拿到锁
mov r1, #1 @r1=1
1: @循环的作用,因SEV是广播事件.不一定lock->rawLock的值已经改变了
ldrex r2, [r0] @r0 = &lock->rawLock, r2 = lock->rawLock
cmp r2, #0 @r20比较
wfene @不相等时,说明资源被占用,CPU核进入睡眠状态
strexeq r2, r1, [r0]@此时CPU被重新唤醒,尝试令lock->rawLock=1,成功写入则r2=0
cmpeq r2, #0 @再来比较r2是否等于0,如果相等则获取到了锁
bne 1b @如果不相等,继续进入循环
dmb @DMB指令来隔离,以保证缓冲中的数据已经落实到RAM
bx lr @此时是一定拿到锁了,跳回调用ArchSpinLock函数
FUNCTION(ArchSpinTrylock) @尝试拿锁
mov r1, #1 @r1=1
mov r2, r0 @r2 = r0
ldrex r0, [r2] @r2 = &lock->rawLock, r0 = lock->rawLock
cmp r0, #0 @r00比较
strexeq r0, r1, [r2] @尝试令lock->rawLock=1,成功写入则r0=0,否则 r0 =1
dmb @数据存储隔离,以保证缓冲中的数据已经落实到RAM
bx lr @跳回调用ArchSpinLock函数
FUNCTION(ArchSpinUnlock) @释放锁
mov r1, #0 @r1=0
dmb @数据存储隔离,以保证缓冲中的数据已经落实到RAM
str r1, [r0] @lock->rawLock = 0
dsb @数据同步隔离
sev @给各CPU广播事件,唤醒沉睡的CPU
bx lr @跳回调用ArchSpinLock函数
FUNCTION(ArchSpinLock) //非要拿到锁
mov r1, #1 //r1=1
1: //循环的作用,因SEV是广播事件.不一定lock->rawLock的值已经改变了
ldrex r2, [r0] //r0 = &lock->rawLock, r2 = lock->rawLock
cmp r2, #0 //r20比较
wfene //不相等时,说明资源被占用,CPU核进入睡眠状态
strexeq r2, r1, [r0]//此时CPU被重新唤醒,尝试令lock->rawLock=1,成功写入则r2=0
cmpeq r2, #0 //再来比较r2是否等于0,如果相等则获取到了锁
bne 1b //如果不相等,继续进入循环
dmb //DMB指令来隔离,以保证缓冲中的数据已经落实到RAM
bx lr //此时是一定拿到锁了,跳回调用ArchSpinLock函数
FUNCTION(ArchSpinTrylock) //尝试拿锁
mov r1, #1 //r1=1
mov r2, r0 //r2 = r0
ldrex r0, [r2] //r2 = &lock->rawLock, r0 = lock->rawLock
cmp r0, #0 //r00比较
strexeq r0, r1, [r2] //尝试令lock->rawLock=1,成功写入则r0=0,否则 r0 =1
dmb //数据存储隔离,以保证缓冲中的数据已经落实到RAM
bx lr //跳回调用ArchSpinLock函数
FUNCTION(ArchSpinUnlock) //释放锁
mov r1, #0 //r1=0
dmb //数据存储隔离,以保证缓冲中的数据已经落实到RAM
str r1, [r0] //lock->rawLock = 0
dsb //数据同步隔离
sev //给各CPU广播事件,唤醒沉睡的CPU
bx lr //跳回调用ArchSpinLock函数
/*!
* @file los_vm_zone.h
* @brief
* @link
@verbatim
@note_pic
鸿蒙地址空间全景图 从 0x00000000U 至 0xFFFFFFFFU ,外设和主存采用统一编址方式
鸿蒙源码分析系列篇: https://blog.csdn.net/kuangyufei
https://my.oschina.net/u/3751245
+----------------------------+ 0xFFFFFFFFU
| IO设备未缓存 |
| PERIPH_PMM_SIZE |
+----------------------------+ 外围设备未缓存基地址 PERIPH_UNCACHED_BASE
| IO设备缓存 |
| PERIPH_PMM_SIZE |
+----------------------------+ 外围设备缓存基地址 PERIPH_CACHED_BASE
| 包括 IO设备 |
| PERIPH_PMM_SIZE |
+----------------------------+ 外围设备基地址 PERIPH_DEVICE_BASE
| Vmalloc段 |
| kernel heap |
| 128M |
| 映射区 |
+----------------------------+ 内核动态分配开始地址 VMALLOC_START
| DDR_MEM_SIZE |
| Uncached段 |
+----------------------------+ 未缓存虚拟空间基地址 UNCACHED_VMM_BASE
| 内核虚拟空间 |
| KERNEL_VMM_SIZE |
| .bss |
| .rodata |
| .text |
| 映射区 |
+----------------------------+ 内核空间开始地址 KERNEL_ASPACE_BASE = KERNEL_VMM_BASE
| 16M预留区 |
+----------------------------+ 用户空间栈顶 USER_ASPACE_TOP_MAX = USER_ASPACE_BASE + USER_ASPACE_SIZE
| |
| 用户空间 |
| USER_ASPACE_SIZE |
| 用户栈区(stack) |
| 映射区(map) |
| 堆区 (heap) |
| .bss |
| .data .text |
+----------------------------+ 用户空间开始地址 USER_ASPACE_BASE
| 16M预留区 |
+----------------------------+ 0x00000000U
以下定义 可见于 ..\vendor\hi3516dv300\config\board\include\board.h
#ifdef LOSCFG_KERNEL_MMU
#ifdef LOSCFG_TEE_ENABLE
#define KERNEL_VADDR_BASE 0x41000000
#else
#define KERNEL_VADDR_BASE 0x40000000
#endif
#else
#define KERNEL_VADDR_BASE DDR_MEM_ADDR
#endif
#define KERNEL_VADDR_SIZE DDR_MEM_SIZE
#define SYS_MEM_BASE DDR_MEM_ADDR
#define SYS_MEM_END (SYS_MEM_BASE + SYS_MEM_SIZE_DEFAULT)
#define EXC_INTERACT_MEM_SIZE 0x100000
内核空间范围: 0x40000000 ~ 0xFFFFFFFF
用户空间氛围: 0x00000000 ~ 0x3FFFFFFF
cached地址和uncached地址的区别是
对cached地址的访问是委托给CPU进行的,也就是说你的操作到底是提交给真正的外设或内存,还是转到CPU缓存,
是由CPU决定的。CPU有一套缓存策略来决定什么时候从缓存中读取数据,什么时候同步缓存。
对unchached地址的访问是告诉CPU忽略缓存,访问操作直接反映到外设或内存上。
对于IO设备一定要用uncached地址访问,是因为你的IO输出操作肯定是希望立即反映到IO设备上,不希望让CPU缓存你的操作;
另一方面,IO设备的状态是独立于CPU的,也就是说IO口状态的改变CPU是不知道,这样就导致缓存和外设的内容不一致,
你从IO设备读取数据时,肯定是希望直接读取IO设备的当前状态,而不是CPU缓存的过期值。
@endverbatim
* @version
* @author weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
* @date 2021-11-30
*/
/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
......@@ -40,97 +122,20 @@ extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/**
* @file los_vm_zone.h
* @brief
* @verbatim
@note_pic
鸿蒙虚拟内存全景图 从 0x00000000U 至 0xFFFFFFFFU
鸿蒙源码分析系列篇: https://blog.csdn.net/kuangyufei
https://my.oschina.net/u/3751245
+----------------------------+ 0xFFFFFFFFU
| IO设备未缓存 |
| PERIPH_PMM_SIZE |
+----------------------------+ 外围设备未缓存基地址 PERIPH_UNCACHED_BASE
| IO设备缓存 |
| PERIPH_PMM_SIZE |
+----------------------------+ 外围设备缓存基地址 PERIPH_CACHED_BASE
| 包括 IO设备 |
| PERIPH_PMM_SIZE |
+----------------------------+ 外围设备基地址 PERIPH_DEVICE_BASE
| Vmalloc段 |
| kernel heap |
| 128M |
| 映射区 |
+----------------------------+ 内核动态分配开始地址 VMALLOC_START
| DDR_MEM_SIZE |
| Uncached段 |
+----------------------------+ 未缓存虚拟空间基地址 UNCACHED_VMM_BASE
| 内核虚拟空间 |
| KERNEL_VMM_SIZE |
| .bss |
| .rodata |
| .text |
| 映射区 |
+----------------------------+ 内核空间开始地址 KERNEL_ASPACE_BASE = KERNEL_VMM_BASE
| 16M预留区 |
+----------------------------+ 用户空间栈顶 USER_ASPACE_TOP_MAX = USER_ASPACE_BASE + USER_ASPACE_SIZE
| |
| 用户空间 |
| USER_ASPACE_SIZE |
| 用户栈区(stack) |
| 映射区(map) |
| 堆区 (heap) |
| .bss |
| .data .text |
+----------------------------+ 用户空间开始地址 USER_ASPACE_BASE
| 16M预留区 |
+----------------------------+ 0x00000000U
以下定义 可见于 ..\vendor\hi3516dv300\config\board\include\board.h
#ifdef LOSCFG_KERNEL_MMU
#ifdef LOSCFG_TEE_ENABLE
#define KERNEL_VADDR_BASE 0x41000000
#else
#define KERNEL_VADDR_BASE 0x40000000
#endif
#else
#define KERNEL_VADDR_BASE DDR_MEM_ADDR
#endif
#define KERNEL_VADDR_SIZE DDR_MEM_SIZE
#define SYS_MEM_BASE DDR_MEM_ADDR
#define SYS_MEM_END (SYS_MEM_BASE + SYS_MEM_SIZE_DEFAULT)
#define EXC_INTERACT_MEM_SIZE 0x100000
内核空间范围: 0x40000000 ~ 0xFFFFFFFF
用户空间氛围: 0x00000000 ~ 0x3FFFFFFF
cached地址和uncached地址的区别是
对cached地址的访问是委托给CPU进行的,也就是说你的操作到底是提交给真正的外设或内存,还是转到CPU缓存,
是由CPU决定的。CPU有一套缓存策略来决定什么时候从缓存中读取数据,什么时候同步缓存。
对unchached地址的访问是告诉CPU忽略缓存,访问操作直接反映到外设或内存上。
对于IO设备一定要用uncached地址访问,是因为你的IO输出操作肯定是希望立即反映到IO设备上,不希望让CPU缓存你的操作;
另一方面,IO设备的状态是独立于CPU的,也就是说IO口状态的改变CPU是不知道,这样就导致缓存和外设的内容不一致,
你从IO设备读取数据时,肯定是希望直接读取IO设备的当前状态,而不是CPU缓存的过期值。
* @endverbatim
*/
#ifdef LOSCFG_KERNEL_MMU
#ifdef LOSCFG_TEE_ENABLE
#define KERNEL_VADDR_BASE 0x41000000
#else
#define KERNEL_VADDR_BASE 0x40000000
#define KERNEL_VADDR_BASE 0x40000000
#endif
#else
#define KERNEL_VADDR_BASE DDR_MEM_ADDR
#define KERNEL_VADDR_BASE DDR_MEM_ADDR ///< 主存基地址 Double Data Rate SDRAM
#endif
#define KERNEL_VADDR_SIZE DDR_MEM_SIZE
#define KERNEL_VADDR_SIZE DDR_MEM_SIZE ///< 主存大小
#define SYS_MEM_BASE DDR_MEM_ADDR
#define SYS_MEM_END (SYS_MEM_BASE + SYS_MEM_SIZE_DEFAULT)
#define SYS_MEM_BASE DDR_MEM_ADDR ///< 物理内存基地址
#define SYS_MEM_END (SYS_MEM_BASE + SYS_MEM_SIZE_DEFAULT) ///< 物理内存大小
#define _U32_C(X) X##U
......@@ -148,7 +153,7 @@ extern "C" {
#define VMALLOC_START (UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE) ///< 堆区基地址
#define VMALLOC_SIZE 0x08000000 ///< 128M
//UART,LCD,摄像头,I2C,中断控制器统称为外部设备
//UART,LCD,摄像头,I2C,中断控制器统称为外部设备, 统一编址
#ifdef LOSCFG_KERNEL_MMU //使用MMU时,只是虚拟地址不一样,但映射的物理设备空间一致.
#define PERIPH_DEVICE_BASE (VMALLOC_START + VMALLOC_SIZE) ///< 不使用buffer,cache
#define PERIPH_DEVICE_SIZE U32_C(PERIPH_PMM_SIZE)
......
git add -A
git commit -m ' 内核模块初始化等级说明
git commit -m ' 对开机汇编代码详细注解
百万汉字注解 + 百篇博客分析 => 挖透鸿蒙内核源码
鸿蒙研究站 | http://weharmonyos.com (国内)
| https://weharmony.github.io (国外)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册