平台: MSM8260
OS: Android 2.3.4
Overview
本文主要描述下kernel exception(这里以oops为主线)的flow以及如何对oops进行分析作一个讲解。
Exception flow
1.1 介绍
android系统上的exception的大致流程:
从arm_notify_die开始,左边是Kernel exception flow, 右边为user space exception flow,也就是说当exception处于user space时,kernel会发送信号给user space, 而不会像kernel exception一样会down机。
另外,我们发现,各种excetpion type和arm的exception基本上是对应的。如data abort, prefetch abort。
从图上看到,不管是哪种异常都会进入arm_notify_die,然后再die。其实经常碰到的exception不会经过arm_notify_die, 而是直接调用了do_page_fault就返回,不过没关系,do_page_fault最终也会调用die或者是force_sig_info。一会我们从代码中就可以看到了。
1.2Recieve
那么系统是如何知道一个exception发生的呢。拿kernel data abort为例(后面都是),当我们在kernel中对一个虚拟地址进行访问的时候,如0x12345678,但在系统初始化的时候,该地址所对应的权限是禁止访问,MMU捕捉到访问0x0的action之后,但是发现初始化时是设置禁止访问的,然后它就设置相关registers,产生一个exception。
1.3 Handler
1.3.1 汇编部分
Ok, exception 产生了, 到了区分是哪种exception的时候。 每种体系结构的exception都不尽相同,
我们是arm平台,所以对应的文件为entry-arm.S@y:\1090\android\kernel\arch\arm\kernel。
Exception时,首先进入异常向量表:
这里暂不讨论vector table放在高端时是如何跳转,毕竟exception为theme嘛,有兴趣的童鞋可参考http://blog.csdn.net/hongtao_liu/article/details/4263176 。所以这里不讨论直接进入vector_dabt + stubs_offset所对应地址:
虽然arm有好多种工作模式,但是在linux上只有两种,user和svc mode。这也使得linux在x86上的处理简单很多,题外话….
Vector_stub为一个宏:
Asm相对c看起来比较吃力(自己不熟悉也是原因咯–!),熬一熬就出头了。大概的意思就是保存当前当前信息(lr, cpsr, r0), 准备进入SVC mode前一些工作(不管是user还是kernel exception时都会进入svc mode), 然后判断产生exception的Mode, 最后跳转到相应handler。
那么我们这里就是进入了__dabt_svc了:
可以看到,不管是user还是kernel exception, 最终都会调用do_DataAbort, 只是kernel exception就reboot了。这里我们要关注的是r0保存了exception的address, r1保存了fault status。至于其他信息,如各种register, 是通过stack传参给do_DataAbort了。
顺便说下,arm中asm到c的传参是通过r0-r3, 当参数多个4个之后,就可以使用stack传递,当然你也可以都用stack传递,没人会说你违法的….
1.3.2 C语言部分
终于熬出头进入了c语言部分,稍稍有点激动,稳住,看fault.c@y:\1090\android\kernel\arch\arm\mm。你会发现里面有do_PrefetchAbort, do_ DataAbort。还有其他一些函数后面会提到。这里以do_ DataAbort举例:
先看其参数,addr就是r0, fsr是r1, 而且pregs就是通过stack传过来的。先执行inf->fn, inf是什么 ?看const struct fsr_info *inf = fsr_info + fsr_fs(fsr);, 你会发现它发现根据fualt status value(fsr变量)去相应的inf,每个fsr_info都有自己的fn,然后通过inf->fn调用。
很多fn都是do_bad,其实它只是个空函数,
这样它就可以执行inf->fn后面的语句也就是arm_notify_die了。
不过从测试当中发现,不管是data abort还是prefect abort,执行的基本上是fsr_info中的do_page_fault函数,因为一般的错误基本上是引用控制值,或者访问了没有访问权限的地址之类。
因此,我们转去看do_page_fault。
其实这个函数主要是处理缺页申请的。它是为虚拟内存管理所服务的。过程为这样子:当取的虚拟地址有效,但是其所对应的也不在当前物理内存,系统必须要从磁盘或者交换文件中装入内存。
对于缺页申请,这里就不描述了。当是use mode的exception时, __do_user_fault会call到force_sig_info发送信号给用户空间,让用户空间处理exception, 然后返回到之前说的汇编部分继续执行被exception打断前的事,当然并不一定是之前的process咯,因为return时会发生schedule嘛!
忘了一件重要事情:oops的log信息是怎么样的我们到现在还没看过呢!内容有点多,sorry,我也不想…
1>[ 24.566404] Unable to handle kernel paging request at virtual address 1234
5678
<1>