【笨叔比特15】ARMv8中哪些蛇神和牛鬼怪处理异常?老和尚:“劈柴挑水做饭。”行者问:“悟道之后呢?”老和尚:“劈柴挑水做饭。””行者又问:“那何谓得道?”老和尚:“未成道,劈柴想挑水,挑水想做饭;”“很多时候平凡的日子不平凡。上期我们讲了ARMv8的异常向量表(上一集点这里)。我们以数据中止异常为例。假设数据中止发生在EL1的异常级别。它会从异常向量表跳转到汇编函数el1_sync。270行的kernel_entry是一个编译宏,用来保存异常场景,将相关的CPU寄存器保存到EL1栈中。这个和ARM32的代码类似,都会有一个stackframe。这个堆栈帧的大小是S_FRAME_SIZE。堆栈帧的大小是由软件定义的,而不是由硬件定义的,这意味着您可以实现与Linux实现不同的堆栈帧。接下来读取esr_el1j寄存器的值。ESR_EL1寄存器的全称是exceptionsyndromeregister,可以参考ARMv8手册的D10.2.36章节。这个寄存器有点类似于ARMv7中的DFSR寄存器。该寄存器的具体定义在D10.2.39章节。在这个寄存器中,EC字段保存的是异常类型(exceptionclass)。当前EL的数据中止,可以参考手册中的定义。当EC==100101时,表示当前EL出现数据中止异常。该值与代码中第273行ESR_ELx_E??C_DABT_CUR的定义一致,定义在arch/arm64/include/asm/esr.h文件中。所以根据274行代码,会跳转到el1_da汇编函数。这个函数也在entry.S文件中。首先读取far_el1寄存器。该寄存器在D10.2.40章中。这个寄存器保存着导致异常发生的虚拟地址,我们的操作系统可以读取这个寄存器来继续后续的异常处理。它与ARMV7中的DFAR寄存器非常相似。第二步是使能中断。第三步跳转到do_mem_abort函数。该函数有三个参数,其中第一个和第二个参数需要注意。第一个参数是异常发生时错误的虚拟地址,即读取的FAR_EL1寄存器,第二个参数是ESR_EL1寄存器。但是第二个参数有很多花样。ESR_EL1寄存器的bits26~31为Exceptionclass,bits0~24为ISSfield。根据异常类,它会有不同的解释。也就是说Exception类不同,ISS字段的编码也不同。.对应Dataabort的类型,ISS的编码在ARMv8手册的2460页。见下图。其中,bits0~5表示发生了哪种类型的数据中止。我们以level3的页表翻译错误为例(translationfaultlevel3),我们看到在DFSC域中,是000111,也就是7号。我们来看看Linux内核代码。arch/arm64/mm/fault.c文件中有一个fault_info[]数组。让我们看看数字7对应的是神马吗?我们惊奇地发现fault_info[]数组中的第7个是level3的translationfault,对应的处理函数是do_page_fault函数,见代码386行。你可以从0开始数这个数组。我们通过笨叔叔的两集把ARMV8异常的前前后后梳理一遍,希望对大家有所帮助。更多精彩内容,敬请关注笨叔叔第二季《奔跑吧Linux内核》配套视频。第二季即将到来。大家期待的第二季视频来了。这次我们是进程管理、锁机制、中断管理三合一,加量不涨价。旗舰篇还是原价1199,现在特价999。初级:笨叔和大家彻底理清了进程管理、锁机制、中断管理相关的概念。例如:一个进程的生命周期进程控制块进程调度的本质CFSscheduler进程切换如何打SMP负载均衡大小和核心调度是怎么回事如何处理上下半部分中断如何写一个中断处理函数什么是软中断?如何使用tasklet和工作队列?什么是中断上下文?如何使用信号量和互斥量,谁应该选择RCU,如何使用RCU,这里为什么要加锁……旗舰篇包含了初级篇的内容,还包括以下特点:对RCU的补充介绍核心代码,真正实现自主可控。在综合创新实验中,本叔带领大家在树莓派上玩起了小OS。面试书,那些年我们被虐的面试题。
