历史上的今天
返回首页

历史上的今天

今天是: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:

[] (dump_backtrace+0x0/0x12c) from [] (dump_stack+0x18/0x1c)

r6:d04d5780 r5:c120c78c r4:c004c070 r3:00000000

[] (dump_stack+0x0/0x1c) from [] (panic+0x80/0x1b4)

[] (panic+0x0/0x1b4) from [] (die+0x1d4/0x210)

r3:c120cbc0 r2:c95d1a60 r1:00000001 r0:c0af3ee8

r7:c0af3ebc

[] (die+0x0/0x210) from [] (__do_kernel_fault.part.2+0x68/0x88)

r8:00000008 r7:c95d1c10 r6:d25b6e00 r5:00000005 r4:00000008

[] (__do_kernel_fault.part.2+0x0/0x88) from [] (do_page_fault+0x204/0x210)

r7:d04d5780 r3:c95d1c10

[] (do_page_fault+0x0/0x210) from [] (do_translation_fault+0xa8/0xb0)

[] (do_translation_fault+0x0/0xb0) from [] (do_DataAbort+0x40/0xac)

r7:c95d1c10 r6:00000008 r5:c1079244 r4:00000005

[] (do_DataAbort+0x0/0xac) from [] (__dabt_svc+0x54/0x80)

结合源码可知其处理流程如下:


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异常处理流程类似。一旦找到源码后,就很容易看懂了。

推荐阅读

史海拾趣

Harvatek Corporation公司的发展小趣事

背景:随着电子技术的飞速发展,Hantronix始终站在技术创新的前沿。公司不断投入研发资源,致力于开发具有自主知识产权的新产品和技术。

发展:Hantronix成功推出了一系列创新产品,如液晶显示器、图形模块、触摸屏等,这些产品不仅性能卓越,而且设计独特,满足了市场多样化的需求。同时,公司还积极申请专利保护,确保其技术成果得到法律的有效保障。

ERP Power公司的发展小趣事

在这个故事中,我们将看到ERP Power如何与HANA技术相结合,为一家电子企业提供了强大的数据支持和分析能力。通过实施基于HANA的ERP Power系统,该企业成功实现了数字化转型,提高了生产效率和产品质量。

ARCOLECTRICSWITCHES公司的发展小趣事

在市场竞争日益激烈的情况下,ARCOLECTRIC SWITCHES公司始终坚持质量至上的原则。公司建立了完善的质量管理体系,从原材料采购到生产加工、产品检验等各个环节都严格把关,确保产品质量符合客户要求。同时,公司不断创新升级产品,推出更加智能化、节能环保的新型开关产品,满足市场不断变化的需求。

GTK UK Ltd公司的发展小趣事
如何设计高效的算法来快速准确地处理光电传感器信号,并控制伺服电机转动,是一个技术难题。
American Power Management Inc公司的发展小趣事

在电子行业的初期,APMI以一家小型创业公司的身份出现,专注于电源管理技术的研发。当时,市场上的电源管理产品普遍存在效率低下、体积庞大的问题。APMI的研发团队通过深入研究,成功开发出了一款高效、紧凑的电源管理芯片,这一创新为电子产品的小型化和节能化奠定了基础。随着技术的不断完善和推广,APMI逐渐在电源管理领域崭露头角,吸引了众多合作伙伴和投资者的关注。

Alcatel-Lucent公司的发展小趣事

在电子行业的初期,APMI以一家小型创业公司的身份出现,专注于电源管理技术的研发。当时,市场上的电源管理产品普遍存在效率低下、体积庞大的问题。APMI的研发团队通过深入研究,成功开发出了一款高效、紧凑的电源管理芯片,这一创新为电子产品的小型化和节能化奠定了基础。随着技术的不断完善和推广,APMI逐渐在电源管理领域崭露头角,吸引了众多合作伙伴和投资者的关注。

问答坊 | AI 解惑

基于DSP的高动态GPS接收机关键技术讨论

GPS是美国建立的高精度全球卫星定位导航系统,在陆地、海洋、航空和航天等领域有着广泛的应用。而高动态GPS接收机则可应用于导弹、卫星、飞机导航等许多场合,但由于高动态GPS接收机涉及军工等敏感领域,故国外的相关技术或产品对我国是封锁的,有 ...…

查看全部问答>

电容深入研究资料集

超级经典,从另一方面深入了解电容的本质与应用!…

查看全部问答>

关于midistrm

请问高手,CMidiStream这个类是做什么用的,我在看一个BSP里有这个类,不知道做什么用的,谢谢!…

查看全部问答>

请教做dm642的高手,dm642 无法从flash 启动的问题

小弟仿ti开发板自己做的板子,1块插上boot_mode0/boot_mode1跳线后可以顺利从flash中启动,而另外一块板子就无法启动阿,请问哪位大哥遇到过类似的问题,什么原因阿?…

查看全部问答>

控制iesimple的滚动条

我在我的应用程序中新创建一个iesimple进程。我获得iesimple的句柄后,在应用程序中发送消息给iesimple,实现控制iesimple能“前进”和“后退”,但我发现iesimple源码中并没有控制其滚动条上下滚动的方法。 我找了点资料,说可以通过得到iesimple ...…

查看全部问答>

WinCE下屏幕保护问题

请问在WinCE下如何进入屏幕保护?是否需要自己写屏保?如果要自己写,怎么运行该屏保,是否需要在WinCE下做些设置?…

查看全部问答>

MSP4250可以不接外部晶振,使MCLK跑到4M吗?

频率精度不用要求太高,能够达到1系列的稳定性都可以 4M ± 20%的稳定性都可以接受。 但是好像4系列的跑4M必须接外部晶体???…

查看全部问答>

刚拿到LaunchPad,不知道能不能给F2XX系列的单片机下载程序呢?

我想知道这个东西到底能做些什么呢?能当成下载器给F2xx系列的芯片下程序吗?如果可以的话,希望可以指导一下,谢谢!…

查看全部问答>

2812的板子是2层的,用CCS4.2和XDS100V2就是连接不上

第一次画2812的小板,刚开始CS6&7(IS62WV51216BLL-55TLI的片选)忘了布线,后来飞线过去, 原理图是按网上下的一个版本画的,就是连不上仿真器。 求熟悉DSP2812的老师给看下,哪里不对啊? 感谢! …

查看全部问答>