历史上的今天
今天是:2025年06月04日(星期三)
2020年06月04日 | arm平台linux异常处理流程
2020-06-04 来源:elecfans
异常处理
异常向量的位置
arch/arm/kernel/traps.c中的early_trap_init函数将异常向量复制到某特定位置,这样当异常发生时,arm就能找到异常向量。
/*
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
* into the vector page, mapped at 0xffff0000, and ensure these
* are visible to the instruction stream.
*/
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
如果看过异常向量,就会发现异常向量总是在函数地址后面加了一个偏移:“+ stubs_offset”。
// 这里定义 stubs_offset
// 这个值是一个固定的值
.equ stubs_offset, __vectors_start + 0x200 - __stubs_start
为什么要 + 0x200?因为early_trap_init函数中在copy的时候也是 + 0x200!就不能用一个宏代替0x200吗!
异常处理入口
异常向量以及异常处理入口在arch/arm/kernel/entry-armv.S中实现
// 这里是中断向量表
.globl __vectors_start
__vectors_start:
ARM( swi SYS_ERROR0 )
THUMB( svc #0 )
THUMB( nop )
W(b) vector_und + stubs_offset
W(ldr) pc, .LCvswi + stubs_offset
W(b) vector_pabt + stubs_offset
W(b) vector_dabt + stubs_offset
W(b) vector_addrexcptn + stubs_offset
W(b) vector_irq + stubs_offset
W(b) vector_fiq + stubs_offset
.globl __vectors_end
__vectors_end:
以data abot为例,vector_dabt是这样实现的:
// 这里定义vector_dabt
vector_stub dabt, ABT_MODE, 8
.long __dabt_usr @ 0 (USR_26 / USR_32)
.long __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __dabt_svc @ 3 (SVC_26 / SVC_32)
.long __dabt_invalid @ 4
.long __dabt_invalid @ 5
.long __dabt_invalid @ 6
.long __dabt_invalid @ 7
.long __dabt_invalid @ 8
.long __dabt_invalid @ 9
.long __dabt_invalid @ a
.long __dabt_invalid @ b
.long __dabt_invalid @ c
.long __dabt_invalid @ d
.long __dabt_invalid @ e
.long __dabt_invalid @ f
vector_stub是一个宏,在本源代码文件前面定义,它展开后是一系列操作,然后根据当前模式进行查表,进而找到__dabt_svc。(当然如果是user模式下则找到__dabt_usr。)
__dabt_usr这样实现:
// 下面定义svc模式下的异常处理
.align 5
__dabt_svc:
svc_entry
mov r2, sp
dabt_helper
svc_exit r5 @ return from exception
UNWIND(.fnend )
ENDPROC(__dabt_svc)
这里不展开描述svc_entry和svc_exit,他们一个在entry_armv.S中定义,一个在entry_header.S中定义。顾名思义,他们用来保存现场和还原现场。
dabt_helper的实现:
.macro dabt_helper
@
@ Call the processor-specific abort handler:
@
@ r2 - pt_regs
@ r4 - aborted context pc
@ r5 - aborted context psr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1. r9 must be preserved.
@
#ifdef MULTI_DABORT
ldr ip, .LCprocfns
mov lr, pc
ldr pc, [ip, #PROCESSOR_DABT_FUNC]
#else
bl CPU_DABORT_HANDLER // 走这个分支,v7_early_abort,./arch/arm/mm/abort-ev7.S
#endif
.endm
abort-ev7.S中实现了v7_early_abort:
.align 5
ENTRY(v7_early_abort)
/*
* The effect of data aborts on on the exclusive access monitor are
* UNPREDICTABLE. Do a CLREX to clear the state
*/
// 清除局部处理器独占标记
clrex
// 设置r0和r1,即设置do_DataAbort的参数
// 其实,这里就是从 cp15 获取异常的状态和类型
// 然后 do_DataAbort 会根据不同的类型和状态做不同的处理
mrc p15, 0, r1, c5, c0, 0 @ get FSR
mrc p15, 0, r0, c6, c0, 0 @ get FAR
......
b do_DataAbort
ENDPROC(v7_early_abort)
do_DataAbort是一个C语言实现的函数。
do_DataAbort
下面是一个svc模式下data abort的dump stack,我根据它分析其处理流程:
Kernel panic - not syncing: Fatal exception
Backtrace:
[ r6:d04d5780 r5:c120c78c r4:c004c070 r3:00000000 [ [ r3:c120cbc0 r2:c95d1a60 r1:00000001 r0:c0af3ee8 r7:c0af3ebc [ r8:00000008 r7:c95d1c10 r6:d25b6e00 r5:00000005 r4:00000008 [ r7:d04d5780 r3:c95d1c10 [ [ r7:c95d1c10 r6:00000008 r5:c1079244 r4:00000005 [ 结合源码可知其处理流程如下: 1、 首先是空指针触发data abort异常。 2、 data abort异常处理调用的第一个C函数是do_DataAbort. 3、 do_DataAbort函数根据fsr的不同调用不同的处理函数,如果处理成功则返回了。 4、 如果处理处理结果不为0,则会看到“Unhandled fault: ...”这样的log。 5、 然后调用arm_notify_die,系统关机或重启了。 asmlinkage void __exception do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { // fsr_info数组使用的是fsr-2level.c中的,见本文件518行 const struct fsr_info *inf = fsr_info + fsr_fs(fsr); // 根据异常类型找到处理方法 struct siginfo info; if (!inf->fn(addr, fsr & ~FSR_LNX_PF, regs)) // 用找到的方法进行处理 return; // 如果处理成功,就返回了 printk(KERN_ALERT "Unhandled fault: %s (0x%03x) at 0x%08lxn", inf->name, fsr, addr); info.si_signo = inf->sig; info.si_errno = 0; info.si_code = inf->code; info.si_addr = (void __user *)addr; arm_notify_die("", regs, &info, fsr, 0); } do_DataAbort的三个参数: 第一个参数addr和第二个参数fsr是这样来的: mrc p15, 0, r1, c5, c0, 0 @ get FSR mrc p15, 0, r0, c6, c0, 0 @ get FAR 要想知道它们的具体含义,须查阅arm手册。 第三个参数regs是通过r2传过来的,那么下面看r2是怎么赋值的: rch/arm/kernel/entry-armv.S中 .align 5 __dabt_svc: svc_entry mov r2, sp dabt_helper svc_exit r5 @ return from exception UNWIND(.fnend ) ENDPROC(__dabt_svc) 可见r2中保存的是sp的值。注意,这个sp应该不是usr态或svc态的sp,而是dabt态的sp。别忘了svc_entry这个宏,它将必要的信息压栈了或做了其它处理。 do_DataAbort函数首先根据fsr在fsr_info数组中找到对应到fsr的结构体并执行其回调函数。如果回调函数返回值为0,表示处理完成,返回。如果回调函数返回值不为0,则继续后续处理。 arm_notify_die 函数会调用 die 函数。 die 函数定义在arch/arm/kernel/traps.c 中。 然后 die 函数又会调用 __die 函数。 __die 函数又会调用print_modules、__show_regs、dump_mem、dump_backtrace、dump_instr 等函数。对照这些函数的源码,相信能够从一大堆log中提取更多有用的信息。 其他类型异常处理 Linux kernel对其他几种异常的处理流程与data abort异常处理流程类似。一旦找到源码后,就很容易看懂了。
下一篇:关于ARMv8另外几个问题
史海拾趣
|
GPS是美国建立的高精度全球卫星定位导航系统,在陆地、海洋、航空和航天等领域有着广泛的应用。而高动态GPS接收机则可应用于导弹、卫星、飞机导航等许多场合,但由于高动态GPS接收机涉及军工等敏感领域,故国外的相关技术或产品对我国是封锁的,有 ...… 查看全部问答> |
|
请教做dm642的高手,dm642 无法从flash 启动的问题 小弟仿ti开发板自己做的板子,1块插上boot_mode0/boot_mode1跳线后可以顺利从flash中启动,而另外一块板子就无法启动阿,请问哪位大哥遇到过类似的问题,什么原因阿?… 查看全部问答> |
|
我在我的应用程序中新创建一个iesimple进程。我获得iesimple的句柄后,在应用程序中发送消息给iesimple,实现控制iesimple能“前进”和“后退”,但我发现iesimple源码中并没有控制其滚动条上下滚动的方法。 我找了点资料,说可以通过得到iesimple ...… 查看全部问答> |
|
频率精度不用要求太高,能够达到1系列的稳定性都可以 4M ± 20%的稳定性都可以接受。 但是好像4系列的跑4M必须接外部晶体???… 查看全部问答> |
|
刚拿到LaunchPad,不知道能不能给F2XX系列的单片机下载程序呢? 我想知道这个东西到底能做些什么呢?能当成下载器给F2xx系列的芯片下程序吗?如果可以的话,希望可以指导一下,谢谢!… 查看全部问答> |
|
2812的板子是2层的,用CCS4.2和XDS100V2就是连接不上 第一次画2812的小板,刚开始CS6&7(IS62WV51216BLL-55TLI的片选)忘了布线,后来飞线过去, 原理图是按网上下的一个版本画的,就是连不上仿真器。 求熟悉DSP2812的老师给看下,哪里不对啊? 感谢! … 查看全部问答> |




