小明同学在一次计算机的实验课里,怎么也出不来实验结果。
小明:老师,老师,这个怎么老是404啊?
老师:你把这个配置文件的某某项填空就行
小明兴高采烈的修改配置文件。
过了一会,小明又嘟囔不行。
老师:你把配置文件发给我。
。。。
老师:小明同学,滚。。。,我让你填空就行,就怎么给我填了一个“空”字
昨天的笨叔点滴里和大家go through一下ARM v7手册和Linux内核代码里异常处理部分的哪些事儿。有小伙伴微信上和笨叔叔说,今晚能不能介绍一下ARMv8上的异常处理,我说好啊,这要看我家小笨笨今天晚上乖不乖了,乖的话,笨叔的空余时间就多一些。
话说ARM v8是和一个ARM v7完全不兼容的架构,但是又偏偏把 ARM v7融入了ARM v8里,说不出什么感觉,至少还没有让笨叔感觉到拍案称绝,但至少64位和32位可以比较完美的融合,也许是为了往后兼容吧,毕竟软件生态最重要。另外一点,ARMv8架构做的越来越成熟了,完全有大家闺秀的风范,完全有能力和其他的架构比一比,这一点从ARM v8的汇编代码可以窥探出来,简洁干练!
ARMv8的最新手册是V8.3版本,全程有6666页,大家一看6666估计都吓坏了,其实大家不用担心,且来听笨叔给你分析分析:
这6666页里有很多内容和ARMv7是重复的,第F和G章是讲ARM v7。另外,ARM的芯片手册是把所有的运行模式都揉在一块来描述,比如我做的系统不用虚拟化,不用secure monitor,那剩下的有用内容就不是很多了。若再把ARM v7和介绍指令集的章节去掉,那这6666页就没有这么恐怖了,对吧?
01
我们来翻开ARM v8手册第D.1章,第一页就告诉你ARM v8有哪些EL,EL是exception level的意思,翻译成中文也许叫异常等级,我们下文还是简称EL等级吧,和芯片手册和代码同步。ARM芯片手册通常使用ELn来表示第n个异常等级。
这里有4个EL,其中EL0是用户态,EL1是内核态也就是特权模式,EL2是虚拟化的hypevisor模式,EL3是安全monitor。是不是和x86的ring0~ring3很像呢?这是向x86前辈致敬吗?哈哈~~
在第D1.2.5章里,介绍了异常主要是分成2大类。一类是同步,另外一类是异步。
这里主要是说,同步异常是:
  1. 导致异常发生的原因是执行了某个指令
  2. 异常返回地址就指明了是因为执行了那个“圈套”指令导致的
  3. 这个异常发生是精确的。
  4. 那啥是异步异常呢:

    1. 这个异常的发生不是因为执行了某个指令而中的圈套
    2. 异常返回地址没法指明是中了哪条指令的圈套
    3. 这个异常发生是不精确的
如果笨叔再用自己的话来翻译一下就是,所谓的同步异常就是说中了指令的圈套,这样的,这些异常的发生是比较精准的。而异步异常是和运行的指令没有关系,因为它是被中圈套。如果用打猎来说的话,你在地里挖个陷阱来打猎,这是同步异常,任何猎物只要跑到这里就中圈套。如果你追着这猎物射箭,对于猎物来说,它什么时候被杀中,它是不知道的,它又不能说 NG重来:笨叔,我们重来,我在前面跑,你重新射我。
所以,我们在ARMv7里说的那些异常,比如data abort等,就很吻合这里说的同步异常,而IRQ和FIQ中断就很吻合异步异常,ARMv8里还有System Error也是属于异步异常。
在D1.10章里包含了很多和异常相关的内容。比如当一个异常发生了之后,ARM处理器做了那些事情?
上面这一大段都是描述异常发生了,ARM处理器做了那些事情。这好比之前那个打猎的例子,不管是守株待兔式的还是射箭式的,中了都要去处理现场吧,万一你的猎物被第三者偷走了,咋办? 上面的步骤,笨叔简单总结一下:
  1. 处理器的状态保存到对应(target)的异常等级的SPSR_ELx寄存器里
  2. 返回地址保存到对应的异常等级的ELR_ELx寄存器里
  3. PSTATE寄存器里的DAIF域都设置为1。PSTATE寄存器是ARM v8里新增的寄存器。
  4. 如果是同步异常,那么究竟什么原因导致的呢?请君看ESR_ELx寄存器。
  5. 设置一下栈指针,指向对应异常等级里的栈。
  6. 迁移到对应的异常等级,然后跑到异常向量表里。
总的来说,这个过程和ARM v7的差不了太多。
接下来,我们看一下异常向量表,究竟和ARM v7的有啥不一样。
在第D1.10.2章里就描述异常向量表了。
这里第一句话是说,这个向量表会占用一大段连续的空间,这是什么鬼呢?我们稍后再解释。
第二段说,每个EL等级里都有一个对应的VBAR寄存器,用来指向异常向量表的基地址。那你一定会疑问,那怎么放呢?
第三段说,异常向量表里必须包含同步异常,Serror,IRQ和FIQ这种信息。
在第D.1.10.2章里有一个表D1-7,这个表,估计很多人看了直接晕倒,太晦涩难懂了。
啥是current EL with SP_EL0,啥是current EL with SP_ELx呢?笨叔刚开始看这个表,也晕,功力不够,之因太笨。呵呵,就像武侠小说里说的,功力不够看武功秘籍会头晕。
还好,ARM公司提供了另外一份比较厚道的文档,那就是《ARM Cortex-A Series Programmer‘s Guide for ARMv8-A》,这个文件相对简单易懂一些。在第10.4章里有描述这个异常向量表是怎么回事的。
她说:在ARM v7的异常向量表,每个表项只有4个字节,只能存放一条跳转指令,但是我们在ARM v8里升级啦,我们每个表项需要128个字节,这样我们可以存放32条指令。小伙伴注意啦,ARM v8指令集里一条指令的位宽是32bit的,而不是64bit,小明同学最近去面试就被面试官鄙视了。异常向量表里要包含4组,每一组包含4个表项。这怎么理解?
比如当前系统只运行Linux内核不包括虚拟化或者安全特性,那最高的EL等级是EL1,那么它就一定有EL0。所以上面说的Current EL就是说的当前系统最高的EL等级。那current EL with SP_EL0是啥意思呢,其实是说,当系统运行在EL1的时候,然后使用EL0的SP,貌似还没有这种场景。current EL with SP_EL1,这是说系统运行在EL1里,SP也是使用EL1,那就是在内核态里发生了异常,这个场景是有的。
第10.4章这个表,比较适合我们去理解上面说的那一大段废话。

举个栗子,当OS运行在内核态时发生了IRQ中断,这时候应该跳转到异常表里的0x280处。如果OS运行在用户态并且正在执行32bit的app,发生了IRQ中断,它应该跳转到哪里呢?
关于异常向量表,还有一个问题需要考虑,那就是异常向量表放在哪里?ARM v8里提供了VBAR(Vector Base address register)。这几个寄存器的描述是在第D10.2章里。
小明同学问:这里有三个相同的VBAR寄存器,我应该设置那个呢?
我们来看一下Linux内核代码(以runninglinux_4.0为例)。代码路径是在arch/arm64/kernel/entry.S文件里。
笨叔在图上圈了4组,就是我们刚才说的那4组。
但是小明同学由有疑问了,笨叔叔,你不是说,每个表项是128个字节吗,我怎么看每个表项好像是4个字节啊?
现在的小明同学已经变得聪明了。
猫腻隐藏在entry这个宏里。
注意这里的127行的align这个伪指令,它的威力很大。这里align 7表示按照2的7次方来对齐,2的7次方是128。
(未完待续,明天我们继续以 data abort异常处理为例,介绍ARM v8上异常处理的那些蛇神牛鬼)
第二季来啦
大家期待的第二季视频来了,我们这次是进程管理、锁机制以及中断管理三合一,加量不加价旗舰篇还是原价1199,现在特价999。三合一初级篇特价299。
初级篇: 笨叔和大家彻底理清进程管理、锁机制以及中断管理相关的概念。比如说:
  1. 进程的生命周期
  2. 进程控制块
  3. 进程调度的本质
  4. CFS调度器
  5. 进程切换是怎么玩
  6. SMP负载均衡
  7. 大小核调度是怎么回事
  8. 为啥需要中断
  9. 中断发生了ARMv7和ARMv8处理器做了啥
  10. 中断底层汇编处理
  11. 中断上下半部
  12. 如何写好一个中断处理函数
  13. 软中断是怎么回事
  14. tasklet和workqueue怎么玩
  15. 什么是中断上下文
  16. 为啥需要锁
  17. 什么是原子操作
  18. ARMv7和ARMv8处理器怎么进行原子操作的
  19. 内存屏障是什么
  20. spinlock怎么用
  21. 信号量和mutex该选谁
  22. RCU怎么用
  23. 为啥这里要添加一个锁
  24. ...
旗舰篇包含初级篇内容,还包括如下内容:
  1. 额外增加核心代码的导读,真正做到自主可控。
  2. 综合创新实验,笨叔带领大家在树莓派上玩一个小OS。
  3. 面试宝典,那些年我们被虐过的面试题目。
现在第二季旗舰篇和初级篇正在火热接收预定,预计8月28号开始陆续发货。
点击“阅读原文”进入订阅页面。
订阅旗舰篇优惠活动:
  1. 已经订阅第一季旗舰篇的小伙伴,订阅第二季旗舰篇可以返回100元现金。同时一起订阅第一季+第二季旗舰篇的小伙伴也是同样返回100元现金。(100元现金是微信返回)
  2. 订阅了第一季+第二季旗舰篇的小伙伴,参加转发笨叔点滴活动可以获得《奔跑吧Linux内核》入门篇签名新书,新书预计2018年双十一或者双十二出版。
[往期精彩]
继续阅读
阅读原文