v40.03 鸿蒙内核源码分析(汇编汇总篇) | 汇编可爱如邻家女孩 | 百篇博客分析OpenHarmony源码

原创
2021/03/09 19:42
阅读数 1.3W

子曰:“三军可夺帅也,匹夫不可夺志也。” 《论语》:子罕篇

在这里插入图片描述

百篇博客系列篇.本篇为: v40.xx 鸿蒙内核源码分析(汇编汇总篇) | 汇编可爱如邻家女孩

硬件架构相关篇为:

汇编其实很可爱

  • 绝大部分IT从业人员终生不用触碰到的汇编,它听着像上古时代遥远的呼唤,总觉得远却又能听到声,汇编再往下就真的是01110011了,汇编指令基本是一一对应了机器指令.
  • 所谓内核是对硬件的驱动,对驱动之后资源的良序管理,这里说的资源是CPU(单核/多核),内存,磁盘,i/o设备.层层封装,步步遮蔽,到了应用层,不知有汉,无论魏晋才好.好是好,但有句话,其实哪有什么岁月静好,只是有人替你负重前行.难道就不想知道别人是怎么负重前行的?
  • 越高级的语言是越接近人思维模式的,越低级的语言就是越贴近逻辑与非门的高低电平的起伏.汇编是贴着硬件飞行的,要研究内核就绕不过汇编,觉得神秘是来源于不了解,恐惧是来自于没接近.
  • 其实深入分析内核源码之后就会发现,汇编其实很可爱,很容易,比c/c++/java容易太多了,真的是很傻很单纯.

鸿蒙内核源码分析系列篇至少已经有五篇涉及到了汇编,请自行翻看,但还是远远不够,要写十五篇,彻底摸透,现在才刚刚开始,本篇先整理鸿蒙内核所有汇编文件和大概说明文件的作用,后续一块一块来剥,不把这些汇编剥个精光不罢休.

汇编目录

鸿蒙所有汇编文件如下: 直接点击可以查看注解源码,有些站点会把链接去除,没办法,可直接去各大站点搜"鸿蒙内核源码分析",找到源码注解.

hw_user_get.S

将用户空间数据src 拷贝到内核空间 dst

// errno_t _arm_get_user(void *dst, const void *src, size_t dstTypeLen, size_t srcTypeLen)
FUNCTION(_arm_get_user)
    stmdb   sp!, {r0, r1, r2, r3, lr} @四个参数入栈,保存LR
    cmp     r2, #0	@r2 和 0比较
    beq     .Lget_user_return @相等 跳到Lget_user_return 直接返回
    cmp     r2, r3	@r2 和 r3比较
    bne     .Lget_user_err	@不等,说明函数要返回错误
    cmp     r2, #1	@r2 和 1比较
    bhi     .Lget_user_half @if(dstTypeLen>1) 跳转到Lget_user_half
.Lget_user_byte:		 @按字节拷贝数据
0:  ldrbt   r3, [r1], #0 @r3=*r1
1:  strb    r3, [r0], #0 @*r0=r3
    b       .Lget_user_return 
.Lget_user_half:
    cmp     r2, #2	@r2 和 2比较
    bhi     .Lget_user_word  @if(dstTypeLen>2) Lget_user_word
2:  ldrht   r3, [r1], #0 	@完成最后一个字节的拷贝
3:  strh    r3, [r0], #0	@完成最后一个字节的拷贝
    b       .Lget_user_return
.Lget_user_word:
    cmp     r2, #4 @r2 和 4比较
    bhi     .Lget_user_err @if(dstTypeLen>4) 跳转到Lget_user_err
4:  ldrt   r3, [r1], #0
5:  str    r3, [r0], #0
.Lget_user_return:	@返回锚点
    ldmia   sp!, {r0, r1, r2, r3, lr} 	@保存的内容出栈,恢复各寄存器值
    mov     r0, 0	@r0保存返回值为0
    bx      lr	@跳回调用函数继续执行,_arm_get_user到此结束!
.Lget_user_err:
    ldmia   sp!, {r0, r1, r2, r3, lr}	@保存的内容出栈,恢复各寄存器值
    mov     r0, #-14	@r0保存返回值为-14	
    bx      lr	@跳回调用函数继续执行,_arm_get_user到此结束!

.pushsection __exc_table, "a"
    .long   0b,  .Lget_user_err
    .long   1b,  .Lget_user_err
    .long   2b,  .Lget_user_err
    .long   3b,  .Lget_user_err
    .long   4b,  .Lget_user_err
    .long   5b,  .Lget_user_err
.popsection

解读

  • 用户空间和内核空间的数据为什么需要拷贝? 这是个经典问题,看了网上的一些回答,没毛病:

    内核不能信任任何用户空间的指针。必须对用户空间的指针指向的数据进行验证。如果只做验证不做拷贝的话,那么在随后的运行中要随时受到其它进/线程可能修改用户空间数据的威胁。所以必须做拷贝。

    在内存系列篇中已经反复的说过,每个用户进程都有自己独立的用户空间,但这个用户空间是通过MMU映射出来的,是表面上繁花似锦,背后都共用着真正的物理内存,所以在高频率的任务切换过程中,原有的用户空间地址内容很容易被覆盖掉.举个例子说明下:

    • 用户A有个美女西施放在万聪酒店21号房说要献给内核大佬,如果内核不直接把美女接回家,而仅仅是做个记录,写着西施在万聪酒店21号房,内核大佬立马跑去过,还不会错能拿对人,但如果被其他事给耽搁了呢?
    • 耽搁的这回功夫,调度算法把万聪酒店21号房给了用户B使用,当然用户B使用之前,酒店管理人员会把西施置换个地方(以至于用户A再回到酒店时,原来的东西该怎样还咋样还原). 等21号房空出来了,B肯定不知道原来的房间是A在用,而且里面曾经还过有个美女西施,更不可能晓得A把西施献给内核大佬这回事了.因为B的业务需要,很可能往21号房整了个东施进来.
    • 此时如果内核大佬事忙完了,想起用户A献美女的事了,是时候了.因为只记录了地址,直接去万聪酒店21号房抓人,可这会抓出来那是咱东施小姐呀.这可不把事给搞砸啦.
    • 所以需要跨空间拷贝,直接把美女接回家找个地方关起来先.

reset_vector_mp.S 和 reset_vector_up.S

鸿蒙开机代码根据 CPU多核还是单核分成了两个独立文件处理. mp就是多处理器(multiprocessing)的意思: 多CPU核的操作系统3种处理模式(SMP+AMP+BMP) 鸿蒙实现的是 SMP 的方式

  • 非对称多处理(Asymmetric multiprocessing,AMP)每个CPU内核 运行一个独立的操作系统或同一操作系统的独立实例(instantiation)。

  • 对称多处理(Symmetric multiprocessing,SMP)一个操作系统的实例 可以同时管理所有CPU内核,且应用并不绑定某一个内核。

  • 混合多处理(Bound multiprocessing,BMP)一个操作系统的实例可以 同时管理所有CPU内核,但每个应用被锁定于某个指定的核心。

up(unit processing )的意思,单个CPU,虽然没mp的复杂,但文件也很大 500行汇编,一小节讲不完,需要单独的一篇专讲 reset_vector

这里只列出up情况下的开机代码

reset_vector: @鸿蒙单核cpu 开机代码
    /* do some early cpu setup: i/d cache disable, mmu disabled */
    mrc     p15, 0, r0, c1, c0, 0
    bic     r0, #(1<<12)
    bic     r0, #(1<<2 | 1<<0)
    mcr     p15, 0, r0, c1, c0, 0

    /* r11: delta of physical address and virtual address */
    adr     r11, pa_va_offset
    ldr     r0, [r11]
    sub     r11, r11, r0

    /* 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) */
    sub     r6, r7                              /* r6: delta of linked address (or vm address) */
    add     r6, r4                              /* r6: end of load address */

los_dispatch.S 和 los_hw_exc.S

异常模式处理入口和统一分发现实,之前也有提到过,很复杂,1000多行,后续单独细说实现过程.

jmp.S

两个简单的函数longjmp setjmp 的实现,加注解部分请前往 鸿蒙内核源码注解分析 查看

FUNCTION(longjmp)
        ldmfd   r0,{r4-r12}
        add     r0,#(4 * 9)
        ldr     r13,[r0]
        add     r0,#4
        ldr     r14,[r0]
        cmp     r1,#0
        moveq   r1,#1
        mov     r0,r1
        mov     pc,lr

FUNCTION(setjmp)
        stmea   r0,{r4-r12}
        add     r0,#(4 * 9)
        str     r13,[r0]
        add     r0,#4
        str     r14,[r0]
        mov     r0,#0
        mov     pc,lr

los_hw_runstop.S

    .global  OsSRSaveRegister
    .global  OsSRRestoreRegister

两个函数的汇编现实,有点复杂,后续单独说明.

cache.S

这是缓存部分的两个函数实现,此处没有加注解,试着看明白这两个函数的实现.加注解部分请前往 鸿蒙内核源码注解分析 查看

.macro  DCACHE_LINE_SIZE, reg, tmp
    mrc     p15, 0, \tmp, c0, c0, 1
    lsr     \tmp, \tmp, #16
    and     \tmp, \tmp, #0xf
    mov     \reg, #4
    mov     \reg, \reg, lsl \tmp
.endm


FUNCTION(arm_inv_cache_range)
    push    {r2, r3}
    DCACHE_LINE_SIZE r2, r3
    sub    r3, r2, #1
    tst    r0, r3
    bic    r0, r0, r3

    mcrne  p15, 0, r0, c7, c14, 1

    tst    r1, r3
    bic    r1, r1, r3
    mcrne  p15, 0, r1, c7, c14, 1
1:
    mcr    p15, 0,  r0, c7, c6, 1
    add    r0,  r0, r2
    cmp    r0,  r1
    blo    1b
    dsb
    pop    {r2, r3}
    mov    pc, lr

FUNCTION(arm_clean_cache_range)
    push   {r2, r3}
    DCACHE_LINE_SIZE r2, r3
    sub    r3, r2, #1
    bic    r0, r0, r3

1:
    mcr    p15, 0,  r0, c7, c10, 1
    add    r0,  r0, r2
    cmp    r0,  r1
    blo    1b
    dsb
    pop    {r2, r3}
    mov    pc, lr

百篇博客分析.深挖内核地基

  • 给鸿蒙内核源码加注释过程中,整理出以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切.确实有难度,自不量力,但已经出发,回头已是不可能的了。 😛
  • 与代码有bug需不断debug一样,文章和注解内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx 代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。

按功能模块:

基础工具 加载运行 进程管理 编译构建
双向链表 位图管理 用栈方式 定时器 原子操作 时间管理 ELF格式 ELF解析 静态链接 重定位 进程映像 进程管理 进程概念 Fork 特殊进程 进程回收 信号生产 信号消费 Shell编辑 Shell解析 编译环境 编译过程 环境脚本 构建工具 gn应用 忍者ninja
进程通讯 内存管理 前因后果 任务管理
自旋锁 互斥锁 进程通讯 信号量 事件控制 消息队列 内存分配 内存管理 内存汇编 内存映射 内存规则 物理内存 总目录 调度故事 内存主奴 源码注释 源码结构 静态站点 时钟任务 任务调度 任务管理 调度队列 调度机制 线程概念 并发并行 CPU 系统调用 任务切换
文件系统 硬件架构
文件概念 文件系统 索引节点 挂载目录 根文件系统 字符设备 VFS 文件句柄 管道文件 汇编基础 汇编传参 工作模式 寄存器 异常接管 汇编汇总 中断切换 中断概念 中断管理

百万汉字注解.精读内核源码

WeHarmony/kernel_liteos_a_note

鸿蒙研究站 | 每天死磕一点点,原创不易,欢迎转载,但请注明出处。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
1 收藏
1
分享
返回顶部
顶部